【问题标题】:Json.NET Custom JsonConverter with data types具有数据类型的 Json.NET 自定义 JsonConverter
【发布时间】:2016-08-26 10:54:18
【问题描述】:

我偶然发现了一个以以下格式输出 JSON 的服务:

{
    "Author": "me",
    "Version": "1.0.0",
    "data.Type1": {
        "Children": [
            {
                "data.Type1": {
                    "Children": [
                        {
                            "data.Type2": {
                                "name": "John",
                                "surname": "Doe"
                            }
                        }
                    ]
                }
            },
            {
                "data.Type3": {
                    "dob": "1990-01-01"
                }
            }
        ]
    }
}

数据类型名称保留为属性名称,它们的值是实际对象。它们都以data. 前缀开头。

之后我想得到的是这样的:

{ // Root
    "Author": "me",
    "Version": "1.0.0",
    "Children": [ // Type1
        {
            "Children": [ // Type1
                { // Type2
                    "Name": "John",
                    "Surname": "Doe"
                }
            ]
        },
        { // Type3
            "DoB": "1990-01-01"
        }
    ]
}

具有以下类:

class Type1 {
    ICollection<object> Children { get; set; }
}

class Type2 {
    public string Name { get; set; }
    public string Surname { get; set; }
}

class Type3 {
    public DateTime DoB { get; set; }
}

class Root 
{
    public string Author { get; set; }
    public string Version { get; set; }
    public Type1 Children { get; set; }
}

问题

如何将其反序列化为添加的 C# 类,同时考虑数据类型并将它们从树中删除?

我已尝试使用自定义 JsonConverter,但我正在努力解决如何动态选择转换器,因为最简单的方法是在属性上放置一个属性,但它不受支持。

一个小例子就好了。

【问题讨论】:

    标签: c# json json.net deserialization


    【解决方案1】:

    虽然这种 JSON 格式有点不寻常,并且由于动态属性名称而拒绝使用属性,但仍然可以通过一个小改动将其反序列化为您喜欢的类结构 JsonConverter:我建议您将Root 类中的Children 属性更改为ICollection&lt;object&gt;,以镜像Type1 类中的Children 属性。现在,它与您所需输出的结构不匹配(其中Children 显示为数组,而不是对象),否则需要转换器中的其他代码才能正确处理。

    class Root
    {
        public string Author { get; set; }
        public string Version { get; set; }
        public ICollection<object> Children { get; set; }
    }
    

    这是我想出的转换器(假设进行了上述更改):

    class CustomConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(Root));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject obj = JObject.Load(reader);
            Root root = new Root();
            root.Author = (string)obj["Author"];
            root.Version = (string)obj["Version"];
            root.Children = ((Type1)DeserializeTypeX(obj, serializer)).Children;
            return root;
        }
    
        private object DeserializeTypeX(JObject obj, JsonSerializer serializer)
        {
            JProperty prop = obj.Properties().Where(p => p.Name.StartsWith("data.")).First();
            JObject child = (JObject)prop.Value;
            if (prop.Name == "data.Type1")
            {
                List<object> children = new List<object>();
                foreach (JObject jo in child["Children"].Children<JObject>())
                {
                    children.Add(DeserializeTypeX(jo, serializer));
                }
                return new Type1 { Children = children };
            }
            else if (prop.Name == "data.Type2")
            {
                return child.ToObject<Type2>(serializer);
            }
            else if (prop.Name == "data.Type3")
            {
                return child.ToObject<Type3>(serializer);
            }
            throw new JsonSerializationException("Unrecognized type: " + prop.Name);
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    有了这个转换器,你可以像这样反序列化你的类:

    Root root = JsonConvert.DeserializeObject<Root>(json, new CustomConverter());
    

    然后您可以像这样序列化为新格式:

    JsonSerializerSettings settings = new JsonSerializerSettings
    {
        DateFormatString = "yyyy-MM-dd",
        Formatting = Formatting.Indented
    };
    
    Console.WriteLine(JsonConvert.SerializeObject(root, settings));
    

    小提琴:https://dotnetfiddle.net/ESNMLE

    【讨论】:

      【解决方案2】:

      不确定这是否可行,但您是否尝试过使用 Newtonsoft.Json 序列化对象并在类属性中包含 JsonProperty 标记?我知道这在将 Json 反序列化为类时会起作用。

      <JsonProperty("user_id")>
      Public Property UserID As String
      //Converts Json {user_id: 123} to class.UserID = 123
      

      要使用 Newtonsoft 进行序列化,首先将 Newtonsoft 包含到项目中,然后

      Dim stringJson As String = Newtonsoft.Json.JsonConvert.SerializeObject(root)
      

      【讨论】:

      • 感谢您的回答。我试过了,但问题是,整个层级都必须被移除和替换,因为它们只是作为数据来区分它们所代表的类型,其中JsonProperty只是在类名和json属性名之间进行映射。
      猜你喜欢
      • 2014-10-09
      • 1970-01-01
      • 2011-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-07
      • 2018-06-05
      • 2016-03-28
      相关资源
      最近更新 更多