C# Newtonsoft.Json 自定义反序列化器

Dae*_*lus 1 c# json json.net

我正在使用一个 API,它以与我习惯处理不同的方式返回结果,而且看起来不标准。

例如,以下是客户数据的片段:

{
    "CustomerID": {
        "value": "EXAMPLE"
    },
    "CustomerCurrencyID": {
        "value": "USD"
    }
}
Run Code Online (Sandbox Code Playgroud)

这个“value”属性似乎非常不必要,所以我想看看我是否可以绕过所有这些并将该 JSON 反序列化为一个对象,如下所示:

class Customer {
    public string CustomerID { get; set; }
    public string CustomerCurrencyID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我目前正在编写一个自定义 JsonConverter 来处理这个问题,所以如果我走的是正确的道路,请告诉我,但这里的任何提示/技巧将不胜感激!

dbc*_*dbc 5

您可以使用通用JsonConverter自定义来执行此操作,如下所示:

public class WrapWithValueConverter<TValue> : JsonConverter
{
    // Here we take advantage of the fact that a converter applied to a property has highest precedence to avoid an infinite recursion.
    class DTO { [JsonConverter(typeof(NoConverter))] public TValue value { get; set; } public object GetValue() => value; }

    public override bool CanConvert(Type objectType) => typeof(TValue).IsAssignableFrom(objectType);
    
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        => serializer.Serialize(writer, new DTO { value = (TValue)value });

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        => serializer.Deserialize<DTO>(reader)?.GetValue();
}

public class NoConverter : JsonConverter
{
    // NoConverter taken from this answer /sf/answers/2781737381/
    // By /sf/users/262092771/
    // To /sf/ask/2781710011/
    public override bool CanConvert(Type objectType)  { throw new NotImplementedException(); /* This converter should only be applied via attributes */ }
    public override bool CanRead => false;
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)

然后您可以将其应用到您的模型中,如下所示:

class Customer {
    [JsonConverter(typeof(WrapWithValueConverter<string>))]
    public string CustomerID { get; set; }
    [JsonConverter(typeof(WrapWithValueConverter<string>))]
    public string CustomerCurrencyID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

演示小提琴#1在这里

或者,如果您希望所有字符串都包装在一个对象中,您可以在序列化和反序列化时{"value": <string value>}添加转换器:JsonSerializerSettings.Converters

var settings = new JsonSerializerSettings
{
    Converters = { new WrapWithValueConverter<string>() },
};

var model = JsonConvert.DeserializeObject<Customer>(json, settings);

var json2 = JsonConvert.SerializeObject(model, Formatting.Indented, settings);
Run Code Online (Sandbox Code Playgroud)

演示小提琴#2在这里

如果您的值是 anenum并且您想将其序列化为字符串,则可以使用以下命令替换NoConverter为:StringEnumConverter

public class WrapEnumWithValueConverter<TEnum> : JsonConverter where TEnum: Enum
{
    // Here we take advantage of the fact that a converter applied to a property has highest precedence to avoid an infinite recursion.
    class DTO { [JsonConverter(typeof(StringEnumConverter))] public TEnum value { get; set; } public object GetValue() => value; }

    public override bool CanConvert(Type objectType) => typeof(TEnum).IsAssignableFrom(objectType);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        => serializer.Serialize(writer, new DTO { value = (TEnum)value });

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        => serializer.Deserialize<DTO>(reader)?.GetValue();
}
Run Code Online (Sandbox Code Playgroud)

演示小提琴#3在这里