Json.net派生型のシリアライズ/デシリアライズ?


98

json.net(newtonsoft)
私はドキュメントを調べていますが、これについて何も見つけることができません。

public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

JsonConvert.Deserialize<List<Base>>(text);

これで、シリアル化されたリストにDerivedオブジェクトがあります。リストを逆シリアル化して派生型を取得するにはどうすればよいですか?


それが継承の仕組みではありません。JsonConvert.Deserialize <Derived>(text);を指定できます。[名前]フィールドを含めます。派生以来IS Aベース(逆ではない)、ベースはの派生定義については何も知りません。
M.バブコック

すみません、少し明確にしました。問題は、ベースオブジェクトと派生オブジェクトの両方を含むリストがあることです。したがって、派生アイテムを逆シリアル化する方法をnewtonsoftにどのように伝えるかを理解する必要があります。
2011

私はこれを解決しました。私も同じ問題を抱えています
ルイスカルロスチャバリア2012

回答:


46

タイプをに保存する場合text(このシナリオではそうする必要があります)、を使用できますJsonSerializerSettings

参照:Newtonsoft JSON.NETを使用してJSONをIEnumerable <BaseType>に逆シリアル化する方法

ただし、注意してください。セキュリティの脆弱性にTypeNameHandling = TypeNameHandling.Noneさらされる可能性があるもの以外を使用すると。


24
使用することもできます。これにより、宣言された型(つまり)がインスタンスの型(つまり)と一致しないインスタンスに対してのみプロパティTypeNameHandling = TypeNameHandling.Autoが追加さ$typeれます。このようにして、JSONをに膨らませることはありません。BaseDerivedTypeNameHandling.All
AJリチャードソン

JSON '...、...'で指定されたエラー解決タイプを受け取り続けます。パス '$ type'、line 1、position 82.何かアイデアはありますか?
briba 2016年

3
セキュリティの問題が発生するため、パブリックエンドポイントでこれを使用する場合は注意してください。alphabot.com
security

1
@gjvdkamp JEEZこのおかげで、私はこれについて知りませんでした。私の投稿に追加されます。
kamranicus

96

タイプ名の処理を有効にし、それを設定パラメーターとして(デ)シリアライザーに渡す必要があります。

Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };

JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);

これにより、派生クラスが正しく逆シリアル化されます。欠点は、使用しているすべてのオブジェクトに名前が付けられることです。そのため、オブジェクトを配置するリストに名前が付けられます。


31
+1。SerializeObjectとDeserializeObjectに同じ設定を使用する必要があることが実際にわかるまで、私は30分間グーグルしました。逆シリアル化するときに$ typeが存在する場合、暗黙的に$ typeを使用すると想定しました。
Erti-Chris Eelmaa

24
TypeNameHandling.Autoそれも行うでしょうし、フィールド/プロパティのタイプと一致するときにインスタンスタイプ名を書き込まないため、より優れています。これは、ほとんどのフィールド/プロパティの場合によく見られます。
ロマン・スターコフ2016

2
これは、別のソリューション/プロジェクトで逆シリアル化が実行されている場合は機能しません。シリアル化では、ソリューションの名前が「SOLUTIONNAME.Models.Model」というタイプとして埋め込まれます。他のソリューションのデシリアライゼーションの上では「JsonSerializationExceptionがスローされます。アセンブリ『SOLUTIONNAME』をロードできませんでした。
悲しいCRUD Developerを

19

質問は非常に人気があるので、タイププロパティ名とその値を制御する場合は、何をすべきかを追加すると役立つ場合があります。

長い道のりはJsonConverter、typeプロパティを手動で確認および設定することにより、シリアル化を処理する(逆)処理を行うカスタムを作成することです。

より簡単な方法は、属性を介してすべてのボイラープレートを処理するJsonSubTypesを使用することです。

[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
    public virtual string Sound { get; }
    public string Color { get; set; }
}

public class Dog : Animal
{
    public override string Sound { get; } = "Bark";
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public override string Sound { get; } = "Meow";
    public bool Declawed { get; set; }
}

3
私は必要に応じていますが、基本クラスにすべての "KnownSubType"を認識させる必要はありません...
Matt Knowles

2
ドキュメントを見れば他のオプションがあります。私はもっ​​と好きな例を提供しただけです。
rzippo

1
これは、シリアル化解除時に任意の型をロードするようにサービスを公開しない、より安全なアプローチです。
デビッドバーグ

3

このJsonKnownTypesを使用します。これは非常によく似た方法であり、jsonに弁別子を​​追加するだけです。

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

これで、jsonでオブジェクトをシリアライズする"$type""base""derived"値が追加され、デシリアライズに使用されます

シリアル化されたリストの例:

[
    {"Name":"some name", "$type":"base"},
    {"Name":"some name", "Something":"something", "$type":"derived"}
]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.