Подтвердить что ты не робот

Как сделать сериализатор JSON.Net для вызова ToString() при сериализации определенного типа?

Я использую сериализатор Newtonsoft.Json для преобразования классов С# в JSON. Для некоторых классов мне не нужен сериализатор для экземпляра для отдельных свойств, а вместо этого просто вызываем ToString для объекта, т.е.

public class Person
{
   public string FirstName { get; set; }
   public string LastName { get; set; }

   public override string ToString() { return string.Format("{0} {1}", FirstName, LastName ); }
}

Что мне делать, чтобы объект Person был сериализован как результат метода ToString()? У меня может быть много таких классов, поэтому я не хочу заканчивать сериализатором, специфичным для класса Person, я хочу иметь его, который может быть применим к любому классу (через атрибут, который я предполагаю).

4b9b3361

Ответ 1

Вы можете сделать это легко с помощью JsonConverter:

public class ToStringJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString());
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Чтобы использовать конвертер, украсьте любые классы, которые должны быть сериализованы как строка с атрибутом [JsonConverter] следующим образом:

[JsonConverter(typeof(ToStringJsonConverter))]
public class Person
{
    ...
}

Ниже приведена демонстрация конвертера в действии:

class Program
{
    static void Main(string[] args)
    {
        Company company = new Company
        {
            CompanyName = "Initrode",
            Boss = new Person { FirstName = "Head", LastName = "Honcho" },
            Employees = new List<Person>
            {
                new Person { FirstName = "Joe", LastName = "Schmoe" },
                new Person { FirstName = "John", LastName = "Doe" }
            }
        };

        string json = JsonConvert.SerializeObject(company, Formatting.Indented);
        Console.WriteLine(json);
    }
}

public class Company
{
    public string CompanyName { get; set; }
    public Person Boss { get; set; }
    public List<Person> Employees { get; set; }
}

[JsonConverter(typeof(ToStringJsonConverter))]
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override string ToString() 
    { 
        return string.Format("{0} {1}", FirstName, LastName); 
    }
}

Вывод:

{
  "CompanyName": "Initrode",
  "Boss": "Head Honcho",
  "Employees": [
    "Joe Schmoe",
    "John Doe"
  ]
}

Если вам также нужно иметь возможность конвертировать из строки обратно в объект, вы можете реализовать метод ReadJson на конвертере таким образом, что он ищет метод public static Parse(string) и вызывает его. Примечание: не забудьте изменить метод конвертера CanRead, чтобы вернуть true (или просто удалить перегрузку CanRead), иначе ReadJson никогда не будет вызываться.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    MethodInfo parse = objectType.GetMethod("Parse", new Type[] { typeof(string) });
    if (parse != null && parse.IsStatic && parse.ReturnType == objectType)
    {
        return parse.Invoke(null, new object[] { (string)reader.Value });
    }

    throw new JsonException(string.Format(
        "The {0} type does not have a public static Parse(string) method that returns a {0}.", 
        objectType.Name));
}

Конечно, для того, чтобы выше работало, вам также нужно будет выполнить подходящий метод Parse для каждого класса, который вы конвертируете, если он еще не существует. Для нашего примера класса Person, показанного выше, этот метод может выглядеть примерно так:

public static Person Parse(string s)
{
    if (string.IsNullOrWhiteSpace(s))
        throw new ArgumentException("s cannot be null or empty", "s");

    string[] parts = s.Split(new char[] { ' ' }, 2);
    Person p = new Person { FirstName = parts[0] };
    if (parts.Length > 1)
        p.LastName = parts[1];

    return p;
}

Демо-версия: https://dotnetfiddle.net/fd4EG4

Ответ 2

У меня нет времени проверить мое решение, но оно должно работать. Предполагая, что все классы, которые вы используете, являются вашими собственными, почему бы вам просто не переопределить ToString для всех, а классы, которые должны использовать сериализатор Newtonsoft.Json, могут быть просто сериализованы в методе ToString и возвращены. Таким образом, вы всегда можете просто вызвать ToString, когда хотите получить сериализованную строку объекта.

Ответ 3

Вы можете просто попробовать библиотеку Builder Newtonsoft JSON и сериализовать объект типа Person, используя такой код:

Dictionary<string, object> collection = new Dictionary<string, object>()
    {
      {"First", new Person(<add FirstName as constructor>)},
      {"Second", new Person(<add LastName as constructor>)},

    };
string json = JsonConvert.SerializeObject(collection, Formatting.Indented, new JsonSerializerSettings
  {
    TypeNameHandling = TypeNameHandling.All,
    TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
  });