反序列化接口实例的集合?

use*_*329 59 c# json.net

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

我想用json.net序列化/反序列化ClassToSerializeViaJson.序列化工作正常,但反序列化给了我这个错误:

Newtonsoft.Json.JsonSerializationException:无法创建ITestInterface类型的实例.Type是接口或抽象类,无法实例化.

那么我该如何反序列化List<ITestInterface>集合呢?

Ben*_*son 82

我在尝试自己做的时候发现了这个问题.在我实施了Piotr Stapp(Garath的)答案之后,我对它看起来多么简单感到震惊.如果我只是实现一个已经传递了我想要实例化的确切类型(作为字符串)的方法,为什么库不能自动绑定它?

我实际上发现我不需要任何自定义绑定器,Json.Net能够完全按照我的需要做,只要我告诉它我正在做的事情.

序列化时:

string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});
Run Code Online (Sandbox Code Playgroud)

反序列化时:

var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects
});
Run Code Online (Sandbox Code Playgroud)

相关文档:Json.NETTypeNameHandling设置的序列化设置

  • 这确实有效,但是人们应该明白,这可能会让你的json大大增加.对我来说,它将输出文件大小增加了10倍以上. (12认同)
  • 请注意,在生成的JSON中放置具体类型名称可以被视为泄漏实现细节,如果在您自己的代码之外的任何地方使用JSON,则绝对不是干净的.此外,如果您反序列化的JSON来自外部源,期望它包含您的类型名称是不合理的. (8认同)
  • 有了这个解决方案,人们应该对传入的类型进行消毒,以避免潜在的安全隐患.有关详细信息,请参阅[Newtonsoft Json中的TypeNameHandling警告](/sf/ask/2769616811/). (3认同)

Pio*_*app 39

Bellow完整的工作示例,包含您想要做的事情:

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

  • 这对我很有用.自此之后的一些更新被回答了一段时间.看起来他们现在正在使用接口`ISerializationBinder`而不是覆盖`SerializationBinder`.同样在`BindToName`中,我使用`serializedType.AssemblyQualifiedName`而不是Name,它将`assemblyName`和完全限定的`typeName`传递给`BindToType`,所以现在不需要构造函数.然后使用`var resolvedTypeName = string.Format("{0},{1}",typeName,assemblyName)更新`BindToType`; 并且一切都应该工作而不在构造函数中提供命名空间和程序集 (3认同)

Inr*_*ego 22

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

来自TypeNameHandling Enumeration文档

自动:当要序列化的对象的类型与其声明的类型不同时,包括.NET类型名称.请注意,默认情况下,这不包括根序列化对象.

  • 这是原始帖子发布后的几年,但这是一种最新的工作方式.您支付的价格是类型名称在JSON中的每个对象上输出为"$ type"属性,但在许多情况下这很好. (2认同)

Eri*_*oom 7

使用默认设置,您不能.JSON.NET无法知道如何反序列化数组.但是,您可以指定要用于接口类型的类型转换器.要了解如何执行此操作,请参阅此页面:http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/

您还可以在此SO问题中找到有关此问题的信息:在JSON.NET中为反序列化转换接口


Nic*_*tby 5

这是一个古老的问题,但我想我会添加一个更深入的答案(以我写的文章的形式):http : //skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet /

TLDR:您可以使用JSON转换器来确定要反序列化哪个类,而不是使用Json.NET在串行化JSON中嵌入类型名称,而是使用您喜欢的任何自定义逻辑。

这样的好处是您可以重构类型而不必担心反序列化的中断。


man*_*c66 5

可以使用JSON.NET和JsonSubTypes属性来完成:

[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")]
public interface ITestInterface
{
    string Guid { get; set; }
}

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

public class Test2 : ITestInterface
{
    public string Guid { get; set; }
    public string Something2 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

并简单地:

var fromCode = new List<ITestInterface>();
// TODO: Add objects to list
var json = JsonConvert.SerializeObject(fromCode);
var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
Run Code Online (Sandbox Code Playgroud)