如何强制将特定 属性 反序列化为字符串而不是数字?

How to force a particular property to be deserialized as string instead of number?

我有不同的后端返回几乎相同的 JSON 数据模型,除了带有 id 字段的嵌套 json 对象,有时可以是纯数字,例如:

{ "something": { "id": 1 } }

或者一个字符串,比如:

{ "something": { "id": "ex.GUID" } }

此外,该模型由可空值、不同的 System 类型和自定义 class 组成。我希望那些 json 数字 1 始终设置为 "1"(因此 C# class 模型将此 属性 设置为 public string Id { get; set; } ), 可能是为了那个精确 属性.

有没有JsonConverter<T>或属性[Attribute]可以小心处理这样的parsing/deserialization?

ps:当前的JsonConverter<object>答案似乎不能很好地处理(例外)上述情况

您必须为此使用自定义 JsonConverter<T>

/// taken from https://www.thecodebuzz.com/system-text-json-create-a-stringconverter-json-serialization/

public class StringConverter : System.Text.Json.Serialization.JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.Number)
        {
            var stringValue = reader.GetInt32();
            return stringValue.ToString();
        }
        else if (reader.TokenType == JsonTokenType.String)
        {
            return reader.GetString();
        }

        throw new System.Text.Json.JsonException();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value);
    }
}

并在 JsonSerializerOptions

中注册
var options = new JsonSerializerOptions
    {
        Converters = { new StringConverter() }, 
        PropertyNameCaseInsensitive = true, 
    };

var jsonA = "{ \"something\": { \"id\": \"ex.GUID\" } }";
var a = JsonSerializer.Deserialize<RootObject>(jsonA, options);

var jsonB = "{ \"something\": { \"id\": 1 } }";
var b = JsonSerializer.Deserialize<RootObject>(jsonB, options);

完整示例in this fiddle

为了完整起见,您可以用 object 来解决这个问题。

using System.Text.Json;

DeserialiseAndPrint("{ \"something\": { \"id\": 1 } }");
DeserialiseAndPrint("{ \"something\": { \"id\": \"ex.GUID\" } }");
DeserialiseAndPrint("{ \"something\": { } }");

void DeserialiseAndPrint(string v)
{
   JsonSerializerOptions options = new (){ PropertyNameCaseInsensitive = true };
   var o = JsonSerializer.Deserialize<RootObject>(v, options);
   Console.WriteLine($"{o?.Something?.Id} -> {o?.Something?.IdParsed}");
}

class RootObject
{
    public Something? Something { get; set; }
}

class Something
{
    public object? Id { get; set; }

    public string? IdParsed => Id?.ToString();
}

这会打印:

1 -> 1
ex.GUID -> ex.GUID
 ->