在阅读一些有关克隆对象的文献时,我碰到了短语
“如果您的对象开箱即用,JSON可序列化”
有人可以解释一下吗?
最好有一些相反的例子(n个对象不是开箱即用的JSON可序列化的对象)
答案 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可序列化”。
编辑: 总而言之,对某些类型的对象进行序列化是没有意义的,因此它们并不是可序列化的。套接字类就是一个。