如何使用Json.NET反序列化一个可以是两种不同数据类型的JSON属性

39

我正在使用Json.NET进行一个项目的开发。从一个外部API接收到的JSON中,有些属性是对象类型,但当它们为空时,则会传递"false"。

例如:

data: {
    supplier: {
        id: 15,
        name: 'TheOne'
    }
}

也可能是:

data: {
    supplier: false
}

我应该如何定义“supplier”属性,使得该属性被反序列化为一个“Supplier”对象或空值。

现在我的代码是这样的:

public class Data {
   [JsonProperty("supplier")]
   public SupplierData Supplier { get; set; }
}
public class SupplierData {
    [JsonProperty("id")]
    public int Id { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
}

现在当尝试反序列化时,如果supplier属性的值为“false”,则会失败。我希望当JSON值为“false”时,Supplier属性的值为null。

希望有人知道怎么做。谢谢。


你可以创建一个自定义的JSON转换器并读取JSON。请参见此链接:https://dev59.com/AmIk5IYBdhLWcg3waNdr - Poornima
1个回答

56

你可以通过为你的SupplierData类创建一个自定义JsonConverter来解决这个问题。以下是该转换器可能的实现:

class SupplierDataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(SupplierData));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Object)
        {
            return token.ToObject<SupplierData>();
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

要使用它,您只需要在Data类中的Supplier属性上添加一个[JsonConverter]属性,就像这样:

public class Data
{
    [JsonProperty("supplier")]
    [JsonConverter(typeof(SupplierDataConverter))]
    public SupplierData Supplier { get; set; }
}

下面展示了转换器的操作演示。请注意,此演示假设您具有某种包含对象来存储 data 属性,因为您提出的 JSON 无法独立存在。我定义了一个名为 RootObject 的类来实现此目的:

public class RootObject
{
    [JsonProperty("data")]
    public Data Data { get; set; }
}

实际演示代码如下:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        {
            ""data"": 
            {
                ""supplier"": 
                {
                    ""id"": 15,
                    ""name"": ""TheOne""
                }
            }
        }";

        Console.WriteLine("--- first run ---");
        RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
        DumpSupplier(obj.Data.Supplier);

        json = @"
        {
            ""data"": 
            {
                ""supplier"": false
            }
        }";

        Console.WriteLine("--- second run ---");
        obj = JsonConvert.DeserializeObject<RootObject>(json);
        DumpSupplier(obj.Data.Supplier);
    }

    static void DumpSupplier(SupplierData supplier)
    {
        if (supplier != null)
        {
            Console.WriteLine("Id: " + supplier.Id);
            Console.WriteLine("Name: " + supplier.Name);
        }
        else
        {
            Console.WriteLine("(null)");
        }
        Console.WriteLine();
    }
}

这里是上述内容的输出结果:

--- first run ---
Id: 15
Name: TheOne

--- second run ---
(null)

4
我有一个类似的案例,其中某个属性可以是对象或空数组。如果没有版本控制,API出现这种情况通常是不好的决定,但这是一个清晰的解决方案。谢谢! - Ran Sagy
@Decoder94 或许你可以发布一个新问题,并详细告诉我们你想要做什么?如果需要,你可以链接回这个问题/答案来提供上下文。 - Brian Rogers
我认为这对于数组/列表类型不起作用。因为它们是通用的,并且在您不需要或不想进行转换的情况下使用。你如何处理这种歧义? - void.pointer
@void.pointer 我不明白你的意思。也许你可以发一个新问题来解释一下? - Brian Rogers
您正在将转换器附加到特定属性,而不是通过设置全局附加,因此我的担忧不成问题。对于造成的困惑,我很抱歉。 - void.pointer
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接