【问题标题】:Custom deserializer only for some fields with json.NET自定义反序列化器仅适用于带有 json.NET 的某些字段
【发布时间】:2017-05-21 11:37:22
【问题描述】:

我正在尝试反序列化一些 JSON:

{ 
   "a":1,
   "b":25,
   "c":"1-7",
   "obj1":{ 
      "a1":10,
      "b1":45,
      "c1":60
   },
   "obj2":[ 
      { 
         "a2":100,
         "b2":15,
         "c2":50
      },
      { 
         "e2":"1,2,5-7",
         "f2":"1,3-5",
         "a2":25
      }
   ]
}

我想找到一种方法来为某些字段定义自定义反序列化。

在下面的代码中,我将需要注意的字段(自定义处理)和可以以某种方式自动完成的字段分开。

是否可以自动反序列化“普通”字段? (不需要任何特定的自定义处理)

[JsonConverter(typeof(ConfigurationSerializer))]
    public class Configuration
    {
        public int a { get; set; }
        public int b { get; set; }
        public Obj1 obj1 { get; set; }

        public int[] c { get; set; }
        public IList<Obj2> obj2 { get; set; }
    }

    public class ConfigurationSerializer : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jsonObject = JObject.Load(reader);

            Configuration configuration = new Configuration();

            // I would like this part to be automatic as I just repeat the default
            // In the real case, I have many fields here!
            configuration.a = (int)jsonObject["a"];
            configuration.b = (int)jsonObject["b"];
            configuration.obj1 = jsonObject["obj1"].ToObject<Obj1>();

            // I created the JsonConverter for those 2 properties
            configuration.c = myCustomProcessMethod(jsonObject["c"]);
            configuration.obj2 = myCustomProcessMethod2(jsonObject["obj2"].ToObject<ValletConfiguration>());

            return configuration;
        }

        public override bool CanConvert(Type objectType)
        {
            return typeof(Configuration).IsAssignableFrom(objectType);
        }
    }

【问题讨论】:

    标签: c# json.net json-deserialization


    【解决方案1】:

    一种可能的方法是创建一个代理属性来正确序列化和反序列化。通过使用ScriptIgnoreAttribute(也可能是JsonIgnoreAttribute),不动产不会被序列化。这是一个例子:

    [ScriptIgnore]
    public int RealProperty { get; set; }
    
    public string RealPropertyProxy
    {
        get
        {
            return SerializeRealProperty(RealProperty);
        }
        set
        {
            RealProperty = DeserializeRealProperty(value);
        }
    }
    

    结果是只有代理按照您定义的方式进行序列化(基于真实属性的值)。只需通过特殊方式修改需要序列化的属性即可,不需要实现特殊的JsonConverter

    【讨论】:

      【解决方案2】:

      由于无论如何您都在使用 Json.NET 属性注释您的类型,一个更简单的解决方案似乎是使用 [JsonConverter(Type)][JsonProperty(ItemConverterType = Type)] 将转换器放在相关属性上:

      public class Configuration
      {
          public int a { get; set; }
          public int b { get; set; }
          public Obj1 obj1 { get; set; }
      
          // Converts the entire list to a compressed string
          [JsonConverter(typeof(IntListConverter))]
          public int[] c { get; set; }
      
          // Converts each Obj2 item individually
          [JsonProperty(ItemConverterType = typeof(Obj2Converter))]
          public IList<Obj2> obj2 { get; set; }
      }
      

      尽管如此,如果您需要在 Configuration 上保留转换器(或者实际上将转换器添加到 JsonSerializerSettings.Converters 并且无法将 Json.NET 属性添加到您的类型),您可以使用 JsonSerializer.Populate() 填充标准属性,只要你先从JObject中删除自定义属性:

          public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
          {
              if (reader.TokenType == JsonToken.Null)
                  return null;
      
              var jsonObject = JObject.Load(reader);
      
              var configuration = (existingValue as Configuration ?? new Configuration());
      
              // I created the JsonConverter for those 2 properties
              configuration.c = myCustomProcessMethod(jsonObject["c"].RemoveFromLowestPossibleParent());
              configuration.obj2 = myCustomProcessMethod2(jsonObject["obj2"].RemoveFromLowestPossibleParent().ToObject<ValletConfiguration>());
      
              // Populate the remaining standard properties
              using (var subReader = jsonObject.CreateReader())
              {
                  serializer.Populate(subReader, configuration);
              }
      
              return configuration;
          }
      

      使用扩展方法:

      public static class JsonExtensions
      {
          public static JToken RemoveFromLowestPossibleParent(this JToken node)
          {
              if (node == null)
                  return null;
              var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
              if (contained != null)
                  contained.Remove();
              // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
              if (node.Parent is JProperty)
                  ((JProperty)node.Parent).Value = null;
              return node;
          }
      }
      

      【讨论】:

      • 感谢您指向using (JsonReader subReader = jObject.CreateReader())。这对我来说似乎是最重要的部分
      • 在创建阅读器副本时要小心。有关问题和解决方案的更多详细信息:stackoverflow.com/a/8031283/11545。也就是说,+1,这很有帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      • 2023-03-29
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多