Десериализовать false как ноль (System.Text. Json) - PullRequest
3 голосов
/ 11 марта 2020

Я использую API, который по какой-то причине использует false, где он должен использовать ноль. Я не могу понять, как правильно десериализовать это. Я попытался создать собственный JsonConverter, чтобы решить эту проблему, но не смог этого сделать. Я хотел бы избежать использования типа Dynami c. Как мне десериализовать это?

Это ответ по умолчанию.

{
    "products": [
        {
            "id": 123456789,
            "supplier": {
                "id": 123456,
                "title": "abc"
            }
        }
    ]
}

Который я десериализирую следующим образом.

public class Container
{
    public Product[] products { get; set; }
}

public class Product
{
    public ulong id { get; set; }
    public Supplier supplier { get; set; }
}

public class Supplier
{
    public ulong id { get; set; }
    public string title { get; set; }
}

JsonSerializer.Deserialize<Container>(json)

И это ответ, когда есть не является поставщиком для продукта.

{
    "products": [
        {
            "id": 123456789,
            "supplier": false
        }
    ]
}

Ответы [ 3 ]

2 голосов
/ 11 марта 2020

Вы можете создать свой пользовательский JsonConverter для свойства supplier:

public class Product
{
    public ulong id { get; set; }

    [JsonConverter(typeof(SupplierConverter))]
    public Supplier supplier { get; set; }
}

public class SupplierConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
       if (reader.TokenType == JsonToken.Boolean)
       {
           if ((bool)reader.Value == false)
               return null;
       }

       return serializer.Deserialize(reader, objectType);
    }

    public override bool CanConvert(Type objectType)
    {
        return false;
    }
}

Обновление:

Если вы используете System.Text.Json, вы можно попробовать следующий пользовательский конвертер:

public class SupplierConverter : JsonConverter<Supplier>
{
    public override Supplier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.False)
            return null;

        if (options.GetConverter(typeof(JsonElement)) is JsonConverter<JsonElement> converter)
        {
            var json = converter.Read(ref reader, typeToConvert, options).GetRawText();

            return JsonSerializer.Deserialize<Supplier>(json);
        }

        throw new JsonException();
    }

    public override void Write(Utf8JsonWriter writer, Supplier value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}
1 голос
/ 11 марта 2020

При использовании System.Text.Json вы можете реализовать и зарегистрировать свой собственный JsonConverter<T> для свойства Supplier.

Существует два способа реализации вашего конвертера, в зависимости от ваших потребностей (независимо от того, необходимо использовать объект Supplier в нескольких местах ИЛИ, если вам нужно использовать другие настройки JsonSerializerOption или нет).

  1. Создайте JsonConverter<Supplier> и добавьте пользовательский лог c для обработки false. Оставьте остальное на Deserialize вызов. В вашей реализации не переходите в варианты. Затем зарегистрируйте этот конвертер в опциях. Это самый простой подход.
// Don't register this converter using an attribute on the Supplier class 
// since you are calling Deserialize on this type directly within the converter.
public class SupplierConverter : JsonConverter<Supplier>
{
    public override Supplier Read(
        ref Utf8JsonReader reader, 
        Type typeToConvert, 
        JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.False)
        {
            return null;
        }

        // Skip passing options here to avoid stackoverflow
        // This approach won't work if you have other options that need to be honored
        // when deserializing Supplier.
        return JsonSerializer.Deserialize<Supplier>(ref reader);
    }

    public override void Write(
        Utf8JsonWriter writer, 
        Supplier value, 
        JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

// Register the converter within options as follows
// and pass the options the JsonSerializer.Deserialize call.
var options = new JsonSerializerOptions
{
    Converters = {new SupplierConverter()}
};
Либо создайте JsonConverter<Supplier> и добавьте пользовательский лог c для обработки «false» вместе с десериализацией объекта Supplier. В этом случае вы можете зарегистрировать этот конвертер либо в параметрах, либо использовать его в качестве атрибута самого класса Supplier. Следуйте этому подходу, если вам по какой-то причине необходимо использовать настройки пользовательских параметров для десериализации поставщика (например, сопоставление имен свойств без учета регистра).
public class SupplierConverter : JsonConverter<Supplier>
{
    public override Supplier Read(
        ref Utf8JsonReader reader, 
        Type typeToConvert, 
        JsonSerializerOptions options)
    {
        // Put whatever special case condition here. 
        // I added a null token check as well, just in case.
        if (reader.TokenType == JsonTokenType.False 
            || reader.TokenType == JsonTokenType.Null)
        {
            return null;
        }

        var output = new Supplier();

        // Potentially add other error handling for invalid JSON, if needed.
        while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
        {
            if (reader.TokenType == JsonTokenType.PropertyName)
            {
                if (reader.ValueTextEquals("id"))
                {
                    if (!reader.Read()) throw new JsonException();
                    output.id = reader.GetUInt64();
                }
                else if (reader.ValueTextEquals("title"))
                {
                    if (!reader.Read()) throw new JsonException();
                    output.title = reader.GetString();
                }
            }
        }

        if (reader.TokenType != JsonTokenType.EndObject)
        {
            throw new JsonException();
        }

        return output;
    }

    public override void Write(
        Utf8JsonWriter writer, 
        Supplier value, 
        JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

// Register the converter within options as follows
// and pass the options the JsonSerializer.Deserialize call.
var options = new JsonSerializerOptions
{
    Converters = {new SupplierConverter()}
};

// OR
// Use annotate your Supplier class with
// a JsonConverterAttribute.

Это делает c будет полезен при написании пользовательских конвертеров:

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to

Вот соответствующие документы API:

Вот рабочий пример (для обоих при json содержит false поставщика и когда он содержит фактический supplier JSON объект в полезной нагрузке): https://dotnetfiddle.net/XFbXB1

0 голосов
/ 11 марта 2020

Если вы используете Json. Net

var settings = new JsonSerializerSettings();
        settings.NullValueHandling = NullValueHandling.Include;
        settings.DefaultValueHandling = DefaultValueHandling.Include;


JsonSerializer.Deserialize<Container>(json,settings)

или попробуйте

var serializeOptions = new JsonSerializerOptions
{
    IgnoreNullValues =false
};

 JsonSerializer.Deserialize<Container>(json,serializeOptions)
...