当C#对象不是JSON可序列化的

时间:2018-12-06 10:04:23

标签: c# json serialization

在阅读一些有关克隆对象的文献时,我碰到了短语

  

“如果您的对象开箱即用,JSON可序列化”

有人可以解释一下吗?

最好有一些相反的例子(n个对象不是开箱即用的JSON可序列化的对象)

3 个答案:

答案 0 :(得分:2)

首先想到的是循环依赖。

比方说,我们有一个类,其中有一个字段引用其子级,而子类中有一个字段引用其父级。

public class A
{
    public B Child;
}

public class B
{
    public A Parent;
}

public class Program
{
    private static void Main()
    {
        A a = new A();
        B b = new B();
        a.Child = b;
        b.Parent = a;

        string json = JsonConvert.SerializeObject(a);
    }
}

这将在运行JsonSerializationException的{​​{1}}时显示以下消息:

JsonConvert.SerializeObject(a)

为避免这种情况,JSON.NET提供了Self referencing loop detected for property 'Parent' with type 'A'. Path 'Child'.的重载来传递设置对象,我们可以在其中指定如何处理循环引用。

SerializeObject

这样,输出json将完全忽略从子级到父级的循环引用,看起来像这样:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    PreserveReferencesHandling = PreserveReferencesHandling.None,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};

JsonConvert.SerializeObject(a, settings);

JSON.NET还提供了一种在不丢失信息的情况下进行处理的方法。我们需要在设置中指定选项{ "Child": {} }

PreserveReferencesHandling.Objects

输出JSON只能由JSON.NET或与JsonSerializerSettings settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }; $id语法兼容的其他序列化器解释,并且看起来像这样:

$ref

答案 1 :(得分:1)

可以序列化和不能序列化什么取决于实现,但是让我们来看看Json.NET的the documentation,它是最常用的JSON序列化库:

  

从高层次上讲,Json.NET序列化程序会将原始.NET值转换为原始JSON值,将.NET数组和集合转换为JSON数组,并将所有其他内容转换为JSON对象。

     

如果反序列化值时遇到不正确的JSON,Json.NET将抛出错误。例如,如果序列化程序遇到带有值数组的JSON属性,而匹配的.NET属性的类型不是集合,则将引发错误,反之亦然。

     

[...]

     

默认情况下,类型的属性在退出模式下序列化。这意味着所有带有getter的公共字段和属性都将自动序列化为JSON,而不应序列化的字段和属性则通过在其上放置JsonIgnoreAttribute来选择退出。要序列化私有成员,可以将JsonPropertyAttribute放在私有字段和属性上。

     

[...]

因此,基本上,如果类成员限于基本的.NET类型,并且没有没有匹配的公共成员的私有成员,那么您应该很好。例如,如果您有private int foo而没有public int Foo { get { return foo; } set { foo = value; } },则数据将在序列化过程中丢失(除非您应用JsonPropertyAttribute)。

阅读我链接的完整文档是值得的,因此您可以更好地了解序列化的工作原理。

答案 2 :(得分:1)

尝试使用Newtonsoft.Json使用TcpClient属性序列化对象:

using Newtonsoft.Json;
using System.Net.Sockets;

class A
{ 
    public TcpClient TcpClient { get; set; } = new TcpClient();
}

var a = new A();

JsonConvert.SerializeObject(a);

它将引发以下异常:

Error getting value from 'MulticastLoopback' on 'System.Net.Sockets.Socket'.

从TcpClient访问MulticastLoopback会引发异常:

var ml = a.TcpClient.Client.MulticastLoopback;

但是如果我们改为使用UdpClient:

class A
{ 
    public UdpClient UdpClient { get; set; } = new UdpClient();
}

var a = new A();
var ml = a.UdpClient.Client.MulticastLoopback; // ok here

JsonConvert.SerializeObject(a); // Exception here

然后访问该特定属性将不会失败,但是它将在以下位置失败

var ls = a.UdpClient.Client.LingerState;

并且序列化将引发异常:

Error getting value from 'LingerState' on 'System.Net.Sockets.Socket'.

UdpClient和TcpClient共享类型为System.Net.Sockets.Socket的公共属性“ .Client”,此类中的某些成员仅对TCP连接有效,而某些成员仅对UDP连接有效。

不能从TCP套接字访问MulticastLoopback,不能从UDP套接字访问LingerState,因此TcpClient和UdpClient并非“开箱即用的JSON可序列化”。

编辑: 总而言之,对某些类型的对象进行序列化是没有意义的,因此它们并不是可序列化的。套接字类就是一个。