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

时间:2017-01-06 16:44:02

标签: c# json.net

我试图反序列化一些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
      }
   ]
}

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

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

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

[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);
        }
    }

2 个答案:

答案 0 :(得分: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; }
}

尽管如此,如果您需要将转换器保留在Configuration上(或者实际上是将转换器添加到JsonSerializerSettings.Converters并且无法将Json.NET属性添加到您的类型中),则可以使用{{3}填充标准属性,只要您首先从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;
    }
}

答案 1 :(得分:2)

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

[ScriptIgnore]
public int RealProperty { get; set; }

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

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