自定义反序列化器仅适用于json.NET的某些字段

dye*_*yes 9 c# json.net json-deserialization

我正在尝试反序列化一些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
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)

我想找到一种方法来定义仅针对某些字段的自定义反序列化.

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

是否可以自动反序列化"正常"字段?(不需要任何特定的自定义处理)

[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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

dbc*_*dbc 6

既然你用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; }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您需要保留转换器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;
    }
Run Code Online (Sandbox Code Playgroud)

使用扩展方法:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)