web-dev-qa-db-ja.com

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
      }
   ]
}

一部のフィールドに対してのみカスタム逆シリアル化を定義する方法を見つけたいと思います。

次のコードでは、注意が必要なフィールド(カスタム処理)と、何らかの方法で自動的に実行できるフィールドを分離しました。

「通常の」フィールドを自動的に逆シリアル化することは可能ですか? (特定のカスタム処理は必要ありません)

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

とにかく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属性をタイプに追加できない場合)は、次のことができます。 JObjectからカスタムプロパティを最初に削除する限り、 JsonSerializer.Populate() を使用して標準プロパティを設定します。

_    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;
    }
}
_
6
dbc

考えられる方法は、プロキシプロパティを作成して、正しくシリアル化および逆シリアル化することです。 ScriptIgnoreAttributeJsonIgnoreAttribute も可能です)を使用すると、実際のプロパティはシリアル化されません。次に例を示します。

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

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

その結果、プロキシのみが(不動産の値に基づいて)定義した方法でシリアル化されます。特別な方法でシリアル化する必要があるプロパティを変更するだけで、特別なJsonConverterを実装する必要はありません。

2
Fruchtzwerg