【问题标题】:Parse non-array JSON object as array with Json.net使用 Json.net 将非数组 JSON 对象解析为数组
【发布时间】:2012-06-11 13:04:34
【问题描述】:

我正在使用一个外部 API,它以数组或对象的形式返回属性,具体取决于计数。有什么好的方法来处理这个问题?

以数组形式返回:

{
    "contacts": {
        "address": [
            {
                "id": "47602070",
                "type": "Work",
                "street": "MyStreet",
                "city": "MyCity",
                "zip": "12345",
                "country": "USA"
            },
            {
                "id": "47732816",
                "type": "GPS",
                "street": "50.0,30.0"
            }
        ]
    }
}

作为对象返回:

{
    "contacts": {
        "address": {
            "id": "47602070",
            "type": "Work",
            "street": "MyStreet",
            "city": "MyCity",
            "zip": "12345",
            "country": "USA"
        }
    }
}

我在想一种解决方法是使用自定义反序列化器并为对象情况返回一个长度为 1 的数组,并为数组情况返回默认反序列化,但我还不知道该怎么做。

我尝试将对象反序列化为数组,并希望 Json.net 能为我处理这种情况,但没有骰子。

【问题讨论】:

    标签: c# arrays json json.net deserialization


    【解决方案1】:

    基于 Christophe Geers 的answer,这是我最终所做的。

    1. 创建自定义 JSON 转换器以始终将 JSON 解析为数组。如果 JSON 是非数组对象,则反序列化该对象并将其包装在一个数组中。

    2. 用自定义转换器属性标记相应的属性。

    自定义转换器

    public class JsonToArrayConverter<T> : CustomCreationConverter<T[]>
    {
        public override T[] Create(Type objectType)
        {
            // Default value is an empty array.
            return new T[0];
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object
            existingValue, JsonSerializer serializer)
        {
    
            if (reader.TokenType == JsonToken.StartArray)
            {
                // JSON object was an array, so just deserialize it as usual.
                object result = serializer.Deserialize(reader, objectType);
                return result;
            }
            else
            {
                // JSON object was not an arry, so deserialize the object
                // and wrap it in an array.
                var resultObject = serializer.Deserialize<T>(reader);
                return new T[] {resultObject};
            }
        }
    }
    

    问题示例的数据结构

    public class Organisation
    {
        public Contacts contacts;
    }
    
    public class Address
    {
        public string id;
        public string street;
        public string city;
        public string type;
        public string zip;
        public string country;
    }
    
    public class Contacts
    {
        // Tell JSON.net to use the custom converter for this property.
        [JsonConverter(typeof(JsonToArrayConverter<Address>))]
        public Address[] address;
    }
    

    【讨论】:

      【解决方案2】:

      一个自定义的 JSON.NET 转换器可能会在这里解决问题。没那么难。

      对于 DateTime 属性,您可以按如下方式进行。只需使用自定义转换器装饰相关属性即可。

      [JsonObject(MemberSerialization.OptIn)]
      public class MyClass
      {
          [JsonProperty(PropertyName = "creation_date")]
          [JsonConverter(typeof(UnixDateTimeConverter))]
          public DateTime CreationDate { get; set; }
      }
      

      JSON.NET 提供了大部分管道。只需从基础转换器派生即可。

      public class UnixDateTimeConverter : DateTimeConverterBase
      {
          public override void WriteJson(JsonWriter writer, object value, 
                                         JsonSerializer serializer)
          { ...}
      
          public override void WriteJson(JsonWriter writer, object value, 
                                         JsonSerializer serializer)
          { ... } 
      }  
      

      您所要做的就是实现 ReadJson(反序列化)和 WriteJson(序列化)方法。

      你可以在这里找到一个完整的例子:

      Writing a custom Json.NET DateTime Converter

      对于您的特定问题,您需要更多控制。尝试以下类型的转换器:

      public class Contact
      { 
         private List<Address> _addresses = new List<Address>();       
         public IEnumerable<Address> Addresses { get { return _addresses; }
      }
      
      public class ContactConverter : CustomCreationConverter<Contact>
      {
          public override Contact Create(Type objectType)
          {
              return new Contact();
          }
      
          public override object ReadJson(JsonReader reader, Type objectType, object 
              existingValue, JsonSerializer serializer)
          {
              var mappedObj = new Contact();
      
              // Parse JSON data here
              // ...
      
              return mappedObj;
          }
      }
      

      使用像上面这样的自定义转换器,您可以自己解析 JSON 数据并根据需要组合联系人对象。

      我修改了一个在这里找到的例子:

      JSON.NET Custom Converters–A Quick Tour

      在这种情况下,你需要在解串时传递自定义转换器。

      Contact contact = 
          JsonConvert.DeserializeObject<Contact>(json, new ContactConverter());
      

      【讨论】:

      • 感谢您的详尽输入。从您的示例中,我得到了它的工作并发布了我的最终结果。
      【解决方案3】:

      注意:您可以只使用普通转换器,而不是使用CustomCreationConverter。例如我使用这样的东西:

      public class SingleToArrayConverter<T> : JsonConverter
      {
          public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
          {
              var items = (IEnumerable<T>)value;
              if (value == null || !items.Any())
              {
                  writer.WriteNull();
              }
              else if (items.Count() == 1)
              {
                  serializer.Serialize(writer, items.ElementAt(0));
              }
              else
              {
                  serializer.Serialize(writer, items);
              }
          }
      
          public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
          {
              if (!CanConvert(objectType))
              {
                  throw new NotSupportedException();
              }
      
              if (reader.TokenType == JsonToken.Null)
              {
                  reader.Skip();
                  return null;
              }
              else if (reader.TokenType == JsonToken.StartObject)
              {
                  return new T[] { serializer.Deserialize<T>(reader) };
              }
              else
              {
                  return serializer.Deserialize<T[]>(reader);
              }
          }
      
          public override bool CanConvert(Type objectType)
          {
              return objectType == typeof(IEnumerable<T>);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-21
        • 2023-03-22
        • 2020-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多