JsonSerializer ведет себя не так, как ожидалось, когда указанный класс приведен к чему-то другому - PullRequest
3 голосов
/ 17 октября 2019

Я пытаюсь перейти с json.net на json от Microsoft и обнаружил что-то, что ведет себя совсем по-другому.

Давайте использовать этот упрощенный пример:

public interface IName
{
    string Name { get; set; }

}

public class Person : IName
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public void Foo() 
{
   IName p = new Person {Age = 4, Name = "Waldo"};
   var s1 = System.Text.Json.JsonSerializer.Serialize(p); // --> {"Name":"Waldo"}
   var s2 = Newtonsoft.Json.JsonConvert.SerializeObject(p); // --> {"Name":"Waldo","Age":4}
}

Сериализаторы Microsoft сериализуют свойства из IName JSON.NET сериализует свойства из Person

Есть ли способ настроить его так, чтобы он работал как JSON.NET? Опции, которые я мог передать, не указывают, что это настраивается. Я что-то упустил?

Ответы [ 4 ]

2 голосов
/ 17 октября 2019

Newtonsoft.Json.JsonConvert.SerializeObject - это не универсальный метод, поэтому он анализирует предоставленный объект во время выполнения и сериализует все свойства, которыми обладает объект.

Для сравнения System.Text.Json.JsonSerializer.Serialize(p) разрешается на универсальный метод . Компилятор выводит параметры типа на основе типа переменной, в случае вашей же IName. Поэтому метод анализирует предоставленный тип и экспортирует свойства параметра универсального типа, а не все свойства объекта, реализующего интерфейс.

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

Следующий код должен исправить поведение:

var s1 = System.Text.Json.JsonSerializer.Serialize(p, p.GetType()); 
// --> {"Name":"Waldo","Age":4}

См. Этот образец .

2 голосов
/ 17 октября 2019

См. Включите свойства производных классов

Полиморфная сериализация не поддерживается, когда вы указываете во время компиляции тип для сериализации.

(примеры здесь)

Это поведение предназначено для предотвращения случайного раскрытия данных в производном типе, созданном во время выполнения.

И затем:

Чтобы сериализовать свойства производного типа, используйте один из следующих подходов:

  • Вызовите перегрузку Serialize, которая позволяет указать тип во время выполнения:

    json = JsonSerializer.Serialize(weatherForecast, weatherForecast.GetType());

  • Объявите объект сериализации как объект.

    json = JsonSerializer.Serialize<object>(weatherForecast);

2 голосов
/ 17 октября 2019

Это связано с тем, что сериализатор использует тип универсального параметра , а не тип переданного значения:

public static string Serialize<TValue>(TValue value, JsonSerializerOptions options = null)
{
    return WriteCoreString(value, typeof(TValue), options);
}

Это передает typeof(IName) в WriteCoreString ипо этому типу в конечном итоге осуществляется рефлексия.

Вы можете обойти это, явно передав тип перегрузке, которая принимает:

var s3 = System.Text.Json.JsonSerializer.Serialize(p, p.GetType());

Возвращает:

{"Name":"Waldo","Age":4}

Приведение к object также работает, так как код затем вызывает value.GetType():

var s4 = System.Text.Json.JsonSerializer.Serialize((object)p);
0 голосов
/ 17 октября 2019

Думаю, вам нужно передать ссылку на объект методу

        IName p = new Person { Age = 4, Name = "Waldo" };
        var s1 = System.Text.Json.JsonSerializer.Serialize<Person>((Person)p);

Для .NET Core 3.0

...