修复 JSON 中的数组返回

Fix Array in JSON Returned

我正在通过 LightSpeed 呼叫联系人 API。

当我打电话询问客户“A”的详细信息时,JSON 包含他的相关电子邮件的以下内容

{
    "Emails": {
        "ContactEmail": {
            "address": "clienta@yahoo.com",
            "useType": "Primary"
        }
    }
}

当我要求客户“B”的详细信息时,JSON 包含此相关电子邮件的以下内容

{
    "Emails": {
        "ContactEmail": [{
                "address": "clientb1@gmail.com",
                "useType": "Primary"
            }, {
                "address": "clientb2@gmail.com",
                "useType": "Secondary"
            }
        ]
    }
}

如果我是对的,我相信第一个响应应该是一个数组,即使只返回了 1 个“电子邮件”...?因为系统允许客户在他们的记录中有超过 1 封电子邮件。

这是我要反序列化的 class。它对客户端“B”非常有效,但对客户端“A”无效

public class GetCustomersResponse
{
    public Attributes attributes { get; set; }
    public List<Customer> Customer { get; set; }
}

public class Attributes
{
    public string count { get; set; }
}

public class Customer
{
    public string customerID { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }
    public string title { get; set; }
    public string company { get; set; }
    public string companyRegistrationNumber { get; set; }
    public string vatNumber { get; set; }
    public DateTime createTime { get; set; }
    public DateTime timeStamp { get; set; }
    public string archived { get; set; }
    public string contactID { get; set; }
    public string creditAccountID { get; set; }
    public string customerTypeID { get; set; }
    public string discountID { get; set; }
    public string taxCategoryID { get; set; }
    public Contact Contact { get; set; }
}

public class Contact
{
    public string contactID { get; set; }
    public string custom { get; set; }
    public string noEmail { get; set; }
    public string noPhone { get; set; }
    public string noMail { get; set; }
    public Addresses Addresses { get; set; }
    public Phones Phones { get; set; }
    public Emails Emails { get; set; }
    public string Websites { get; set; }
    public DateTime timeStamp { get; set; }
}

public class Addresses
{
    public Contactaddress ContactAddress { get; set; }
}

public class Contactaddress
{
    public string address1 { get; set; }
    public string address2 { get; set; }
    public string city { get; set; }
    public string state { get; set; }
    public string zip { get; set; }
    public string country { get; set; }
    public string countryCode { get; set; }
    public string stateCode { get; set; }
}

public class Phones
{
    public List<Contactphone> ContactPhone { get; set; }
}

public class Contactphone
{
    public string number { get; set; }
    public string useType { get; set; }
}

public class Emails
{
    public List<Contactemail> ContactEmail { get; set; }
}

public class Contactemail
{
    public string address { get; set; }
    public string useType { get; set; }
}

我看不到我让 LightSpeed 改变他们的 API 所以任何人都可以建议如何让具有 1 个电子邮件地址的客户与我的 class 一起工作吗?

如有任何帮助,我们将不胜感激。

更新:

在提供的帮助下,我已经非常接近一些工作代码了。

这是我的自定义 json 转换器:

public class ContactEmailJsonConverter : JsonConverter<List<ContactEmail>>
{
    public override List<ContactEmail> Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        try
        {
            if (reader.TokenType == JsonTokenType.StartArray)
            {
                return (List<ContactEmail>)JsonSerializer
                    .Deserialize(ref reader, typeToConvert, options);
            }
            else if (reader.TokenType == JsonTokenType.StartObject)
            {
                var email = (ContactEmail)JsonSerializer
                    .Deserialize(ref reader, typeof(ContactEmail), options);
                return new List<ContactEmail>(capacity: 1) { email };
            }
            else
            {
                throw new InvalidOperationException($"got: {reader.TokenType}");
            }
        }
        catch(Exception ex)
        {
            return null;
        }
    }

    public override void Write(Utf8JsonWriter writer, List<ContactEmail> value, JsonSerializerOptions options)
    {
        if ((value is null) || (value.Count == 0))
        {
            JsonSerializer.Serialize(writer, (ContactEmail)null, options);
        }
        else if (value.Count == 1)
        {
            JsonSerializer.Serialize(writer, value[0], options);
        }
        else
        {
            JsonSerializer.Serialize(writer, value, options);
        }
    }
}

但是,我现在找到了一个似乎根本没有电子邮件的联系人。LightSpeed 返回的 JSON 看起来像这样:

 "Emails":""

它破坏了我编写的转换器代码。我不确定如何处理这个完全空的对象?

我相信你需要的是转换器。这是一个快速而肮脏的方法,所以我不是 100%,但我相信它应该让你非常接近(而且我没有费心去另一个方向,只是实施 Read,我想你明白了要点来自这个)。

要使用它,您需要使用转换器标记您的 Emails class:

public class Emails
{
    [JsonConverter(typeof(ContactEmailJsonConverter))]
    public Contactemail[] ContactEmail { get; set; }
}

然后像这样写转换器。我在这里所做的基本上是在开头寻找 [ ,如果它不存在,则在反序列化之前将其包装。

public class ContactEmailJsonConverter : JsonConverter<Contactemail[]>
{
    public override Contactemail[] Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options) =>
        {
            string content = reader.GetString();
            if(!content.Trim().StartsWith("["))
            {
                content = $"[{content}]";
            }
            return JsonSerializer.Deserialize<Contactemail[]>(content);
        };

    public override void Write(
        Utf8JsonWriter writer,
        Contactemail[] cotactEmails,
        JsonSerializerOptions options) =>
            throw new NotImplementedException();
}

您或许可以让它更健壮,但这应该让您入门。

注意:我确实将类型更新为 Contactemail[],因为我认为 List<T> 在转换器方面有点复杂。下面链接的文档声明使用“工厂模式”并给出了示例,因此如果您想坚持使用 List<T>,您可以改为遵循它。

更多文档:https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0

您要做的是创建几个自定义 JsonConverter 对象。假设您的模型如下所示:

public class ContactEmail
{
    [JsonPropertyName("address")]
    public string Address { get; set; }

    [JsonPropertyName("useType")]
    public string UseType { get; set; }
}

public class Emails
{
    public List<ContactEmail> ContactEmail { get; set; }
}

public class Root
{
    public Emails Emails { get; set; }
}

首先,您需要设置一个自定义转换器来处理 List<ContactEmail> 以及可能是数组或单个对象的奇怪实例:

public sealed class ContactEmailListJsonConverter 
    : JsonConverter<List<ContactEmail>>
{
    public override List<ContactEmail> Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        if(reader.TokenType == JsonTokenType.StartArray)
        {
            return (List<ContactEmail>)JsonSerializer
                .Deserialize(ref reader, typeToConvert, options);
        }
        else if (reader.TokenType == JsonTokenType.StartObject)
        {
            var email = (ContactEmail)JsonSerializer
                .Deserialize(ref reader, typeof(ContactEmail), options);
            return new List<ContactEmail>(capacity: 1) { email };
        }
        else
        {
            throw new InvalidOperationException($"got: {reader.TokenType}");
        }
    }

    public override void Write(
        Utf8JsonWriter writer,
        List<ContactEmail> value,
        JsonSerializerOptions options)
    {
        if((value is null) || (value.Count == 0))
        {
            JsonSerializer.Serialize(writer, (ContactEmail)null, options);
        }
        else if(value.Count == 1)
        {
            JsonSerializer.Serialize(writer, value[0], options);
        }
        else
        {
            JsonSerializer.Serialize(writer, value, options);
        }
    }
}

其次,您需要设置一个自定义转换器来处理 Emails 以及它不是实际 Emails 对象的奇怪实例:

public sealed class EmailsJsonConverter : JsonConverter<Emails>
{
    public override Emails Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        // MUST be an object!
        if(reader.TokenType == JsonTokenType.StartObject)
        {
            return (Emails)JsonSerializer
                    .Deserialize(ref reader, typeToConvert, options);
        }
        // if it's not an object (ex: string), then just return null
        return null;
    }

    public override void Write(
        Utf8JsonWriter writer,
        Emails value,
        JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, options);
    }
}

然后您将转换器添加到您的模型中:

public class Emails
{
    [JsonConverter(typeof(ContactEmailListJsonConverter))]
    public List<ContactEmail> ContactEmail { get; set; }
}

public class Root
{
    [JsonConverter(typeof(EmailsJsonConverter))]
    public Emails Emails { get; set; }
}

然后简单地反序列化:

var obj = JsonSerializer.Deserialize<Root>(jsonData);