76

json.net 経由でこのコードをシリアル化したい:

public interface ITestInterface
{
    string Guid {get;set;}
}

public class TestClassThatImplementsTestInterface1
{
    public string Guid { get;set; }
}

public class TestClassThatImplementsTestInterface2
{
    public string Guid { get;set; }
}


public class ClassToSerializeViaJson
{
    public ClassToSerializeViaJson()
    {             
         this.CollectionToSerialize = new List<ITestInterface>();
         this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
         this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
    }
    List<ITestInterface> CollectionToSerialize { get;set; }
}

ClassToSerializeViaJson を json.net でシリアライズ/デシリアライズしたい。シリアライゼーションは機能していますが、デシリアライズすると次のエラーが発生します。

Newtonsoft.Json.JsonSerializationException: タイプ ITestInterface のインスタンスを作成できませんでした。型はインターフェイスまたは抽象クラスであり、インスタンス化できません。

List<ITestInterface>では、どうすればコレクションを逆シリアル化できますか?

4

9 に答える 9

43

あなたがしたいことを完全に実行する例を以下に示します。

public interface ITestInterface
{
    string Guid { get; set; }
}

public class TestClassThatImplementsTestInterface1 : ITestInterface
{
    public string Guid { get; set; }
    public string Something1 { get; set; }
}

public class TestClassThatImplementsTestInterface2 : ITestInterface
{
    public string Guid { get; set; }
    public string Something2 { get; set; }
}

public class ClassToSerializeViaJson
{
    public ClassToSerializeViaJson()
    {
        this.CollectionToSerialize = new List<ITestInterface>();
    }
    public List<ITestInterface> CollectionToSerialize { get; set; }
}

public class TypeNameSerializationBinder : SerializationBinder
{
    public string TypeFormat { get; private set; }

    public TypeNameSerializationBinder(string typeFormat)
    {
        TypeFormat = typeFormat;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = null;
        typeName = serializedType.Name;
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        var resolvedTypeName = string.Format(TypeFormat, typeName);
        return Type.GetType(resolvedTypeName, true);
    }
}

class Program
{
    static void Main()
    {
        var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
        var toserialize = new ClassToSerializeViaJson();

        toserialize.CollectionToSerialize.Add(
            new TestClassThatImplementsTestInterface1()
            {
                Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
            });
        toserialize.CollectionToSerialize.Add(
            new TestClassThatImplementsTestInterface2()
            {
                Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
            });

        string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented, 
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                Binder = binder
            });
        var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json, 
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                Binder = binder 
            });

        Console.ReadLine();
    }
}
于 2013-04-08T14:19:52.000 に答える
27

また、Garath の単純さに驚き、Json ライブラリーが自動的に実行できるという結論に達しました。しかし、Ben Jenkinsonの答えよりもさらに簡単だとも思いました(jsonライブラリの開発者自身によって変更されていることがわかりますが)。私のテストでは、次のように TypeNameHandling を Auto に設定するだけで済みます。

var objectToSerialize = new List<IFoo>();
// TODO: Add objects to list
var jsonString = JsonConvert.SerializeObject(objectToSerialize,
       new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString, 
       new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });

TypeNameHandling Enumeration のドキュメントから

自動: シリアル化されるオブジェクトの型が宣言された型と同じでない場合、.NET 型名を含めます。デフォルトでは、これにはルートのシリアル化されたオブジェクトが含まれないことに注意してください。

于 2015-05-23T00:30:19.983 に答える
7

これは古い質問ですが、(私が書いた記事の形で) より詳細な回答を追加すると思いました: http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet /

TLDR:シリアル化された JSON に型名を埋め込むように Json.NET を構成するのではなく、JSON コンバーターを使用して、好きなカスタム ロジックを使用して、どのクラスに逆シリアル化するかを判断できます。

これには、デシリアライゼーションの破損を心配することなく型をリファクタリングできるという利点があります。

于 2016-05-06T01:44:55.880 に答える
7

デフォルト設定を使用することはできません。JSON.NET には、配列を逆シリアル化する方法を知る方法がありません。ただし、インターフェイスの型に使用する型コンバーターを指定できます。これを行う方法については、次のページを参照してください: http://blog.greatreexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/

この問題に関する情報は、SO question: Casting interfaces for deserialization in JSON.NETでも確認できます。

于 2013-04-08T14:00:11.057 に答える
3

Inregoの回答とほぼ重複していますが、さらに説明する価値があります:

使用する場合は、必要なTypeNameHandling.Autoときにのみ型/アセンブリ名が含まれます(つまり、インターフェイスと基本/派生クラス)。したがって、JSON はよりクリーンで、より小さく、より具体的になります。

XML/SOAP に対する主なセールス ポイントの 1 つではないのはどれですか?

于 2016-02-24T21:37:56.973 に答える