Когда объект c # не поддерживает сериализацию в формате JSON - PullRequest
0 голосов
/ 06 декабря 2018

Читая литературу о клонировании объектов, я натолкнулся на фразу

"Если ваш объект из коробки сериализуем JSON"

Может кто-нибудь объяснитьэто, пожалуйста?

С некоторым противоположным примером предпочтительно (случай объекта, который не из коробки JSON сериализуем)

Ответы [ 3 ]

0 голосов
/ 06 декабря 2018

Первое, что приходит на ум, - это циклические зависимости.

Допустим, у нас есть класс, у которого есть поле, ссылающееся на его потомка, а у дочернего класса есть поле, ссылающееся на его родителя.

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 в JsonConvert.SerializeObject(a) со следующим сообщением:

Self referencing loop detected for property 'Parent' with type 'A'. Path 'Child'.

Чтобы избежать этого, JSON.NET предоставляет перегрузку SerializeObject передать объект настроек, где мы можем указать, как обрабатывать циклические ссылки.

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

JsonConvert.SerializeObject(a, settings);

Таким образом, выходной json будет полностью игнорировать циклическую ссылку от дочернего элемента к родительскому, и он будет выглядеть следующим образомэто:

{
    "Child": {}
}

JSON.NET также предоставляет способ обрабатывать их без потери информации.Нам нужно указать в настройках опцию PreserveReferencesHandling.Objects.

JsonSerializerSettings settings = new JsonSerializerSettings
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects
};

Выходной JSON может интерпретироваться только JSON.NET или другими сериализаторами, совместимыми с синтаксисом $id и $ref,и это будет выглядеть так:

{
    "$id":"1",
    "Child": {
        "$id":"2",
        "Parent": {
            "$ref":"1"
        }
    }
}
0 голосов
/ 06 декабря 2018

Попробуйте сериализовать объект со свойством TcpClient, используя Newtonsoft.Json:

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'.

При обращении к MulticastLoopback из TcpClient выдается исключение:

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 имеют общее свойство ".Client", имеющее тип System.Net.Sockets.Socket, и некоторые члены этого класса действительны только для TCP-соединений, а некоторые только для UDP-соединений.

MulticastLoopback не может быть доступен из TCP-сокетов, а LingerState не доступен из UDP-сокетов, поэтому TcpClient и UdpClient не являются "из коробки JSON serializable".

EDIT: Подводя итог, сериализация длянекоторые типы объектов не имеют смысла, поэтому они не предназначены для сериализации.Класс сокетов.

0 голосов
/ 06 декабря 2018

Что можно и нельзя сериализовать, зависит от реализации, но давайте взглянем на документацию для Json.NET, которая является наиболее часто используемой библиотекой сериализации JSON:

На высоком уровне сериализатор Json.NET преобразует примитивные значения .NET в примитивные значения JSON, преобразует массивы и коллекции .NET в массивы JSON и преобразует все остальное в объекты JSON.

Json.NET выдаст ошибку, если при десериализации значения обнаружит неверный JSON.Например, если сериализатор обнаруживает свойство JSON с массивом значений и тип соответствующего свойства .NET не является коллекцией, будет выдано сообщение об ошибке, и наоборот.

[...]

По умолчанию свойства типа сериализуются в режиме отказа.Это означает, что все открытые поля и свойства с геттерами автоматически сериализуются в JSON, а поля и свойства, которые не должны быть сериализованы, отключаются путем размещения на них JsonIgnoreAttribute.Чтобы сериализовать закрытые члены, атрибут JsonPropertyAttribute можно поместить в закрытые поля и свойства.

[...]

Таким образом, в основном, если члены класса ограничены базовыми типами .NETи нет частных участников без подходящего публичного участника, вам нужно идти.Например, если у вас есть private int foo без public int Foo { get { return foo; } set { foo = value; } }, данные будут потеряны во время сериализации (если вы не примените JsonPropertyAttribute).

Стоит прочитать весь документ, на который я ссылался, так что у вас получшепонимание того, как работает сериализация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...