【问题标题】:How to make JSON.Net serializer to call ToString() when serializing a particular type?序列化特定类型时如何使 JSON.Net 序列化程序调用 ToString()?
【发布时间】:2023-03-06 01:41:02
【问题描述】:

我正在使用 Newtonsoft.Json 序列化程序将 C# 类转换为 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 类的序列化程序,我想要一个可以适用于任何类的序列化程序(我猜是通过属性)。

【问题讨论】:

    标签: c# json serialization json.net tostring


    【解决方案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

    【讨论】:

    • 非常感谢。正是我需要的。像魅力一样工作。
    • @BrianRogers 感谢您的精彩回答。我想知道您是否可以就如何在转换器中实现 ReadJson() 提供任何见解,以便将字符串反序列化回其两个各自的属性?
    • 一开始我发现这对我不起作用,但这是因为我的 ToString() 方法用“new”而不是“override”装饰(不知道为什么)——一旦我更正了它工作得很好。感谢分享。
    【解决方案2】:

    如果不打算大规模使用,有一种更快的方法可以做到这一点,在下面的示例中,它是针对 RecordType 属性完成的

    [JsonIgnore]
    public RecordType RecType { get; set; }
    
    [JsonProperty(PropertyName = "RecordType")]
    private string RecordTypeString => RecType.ToString();
    

    【讨论】:

      【解决方案3】:

      您可以简单地尝试 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
        });
      

      【讨论】:

      • 谢谢。我采用了 Brian 建议的方法(使用自定义转换器)。
      【解决方案4】:

      我没有时间测试我的解决方案,但它应该可以工作。假设您使用的所有类都是您自己的,为什么不对所有类进行 ToString 覆盖,而需要使用 Newtonsoft.Json 序列化程序的类可以在 ToString 方法中序列化并返回。这样,当你想要获取对象的序列化字符串时,你总是可以只调用 ToString 。

      【讨论】:

      • 首先,我不想混用 ToString 和 Serialize。他们有不同的目的。其次,我可以有复杂的对象图,所以我应该依靠序列化器来遍历图并应用序列化算法。用你的方法这是行不通的。
      猜你喜欢
      • 1970-01-01
      • 2018-01-16
      • 2013-10-02
      • 1970-01-01
      • 2017-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多