JSON DataContract 双类型,用于字符串和字符串数组。

huangapple go评论74阅读模式
英文:

JSON DataContract dual types for string and string array

问题

Endpoint A response:

{
  "message": "Hello World."
}

Endpoint B response:

{
  "message": [
    "Hello World.",
    "Hello StackOverflow."
  ]
}
[DataContract]
public class Response
{
  [DataMember(Name = "message")]
  public object Message { get; set; } // Keep the Message property as object
}

Error message:

There was an error deserializing the object of type Response. End element 'message' from namespace '' expected. Found element 'item' from namespace ''.

For the sake of completion here's the code:

string jsonPayload = "{ 'Random': 'Payload' }";
HttpClient myHttpClient = getHttpClient();
HttpResponseMessage responseMsg = await myHttpClient.PostAsync("myApiPath", new StringContent(jsonPayload));

DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response));
string rspJson = await responseMsg.Content.ReadAsStringAsync();
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(rspJson));
Response rsp = (Response)serializer.ReadObject(ms);
英文:

I'm consuming a REST API from an adventurous team. They're providing two endpoints where both return a similiar but not equal response. I'm deserializing the responses using the DataContractJsonSerializer.

Endpoint A response:

{
  "message": "Hello World."
}

Endpoint B response:

{
  "message": [
    "Hello World.",
    "Hello StackOverflow."
  ]
}

As you can see endpoint A provides a single string in the message property while endpoint B provides a string array.

I really really want to use the same DataContract but is there a way to make this happen?

[DataContract]
public class Response
{
  [DataMember(Name = "message")]
  public string Message { get; set; } // Changing this to string[] fails as well.
}

Of course I'm getting an error:

> There was an error deserializing the object of type Response. End element 'message' from namespace '' expected. Found element 'item' from namespace ''.

For the sake of completion here's the code:

string jsonPayload = "{ 'Random': 'Payload' }";
HttpClient myHttpClient = getHttpClient();
HttpResponseMessage responseMsg = await myHttpClient.PostAsync("myApiPath", new StringContent(jsonPayload));

DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response));
string rspJson = await responseMsg.Content.ReadAsStringAsync();
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(rspJson));
Response rsp = (Response)serializer.ReadObject(ms);

答案1

得分: 1

DataContractJsonSerializer内置支持多态基元类型和基元类型数组,因此,如果您将Messages属性声明为object,您将能够反序列化JSON:

[DataContract]
public class Response
{
    [DataMember(Name = "message")]
    public object Message { get; set; } // 将此更改为string[]也会失败。
}

演示示例 #1 在此处

这个模型并没有真正捕捉到Message应该是一个字符串或字符串数组的事实,因此,您可能更喜欢使用一些代理属性进行序列化,像这样:

[DataContract]
public class Response
{
    [IgnoreDataMember]
    public string[] Messages { get; set; }
    
    [DataMember(Name = "message")]
    object SerializedMessages 
    { 
        get => Messages; 
        set => Messages = (value) switch
        {
            null => null,
            string s => new [] { s },
            string[] a => a,
            // 将基元类型数组转换为字符串
            object[] a => a.Cast<IFormattable>().Select(f => Convert.ToString(f, CultureInfo.InvariantCulture)).ToArray(),
            _ => throw new ArgumentException(string.Format("Unknown value type {0}", value)),
        };
    }
}

请注意,根据文档的说法:

对于大多数涉及到JSON序列化和JSON反序列化的情况,我们建议使用System.Text.Json命名空间中的API

演示示例 #2 在此处

英文:

DataContractJsonSerializer has built-in support for polymorphic primitives, and arrays of primitives, so if you declare your Messages property as an object you will be able to deserialize either JSON:

[DataContract]
public class Response
{
	[DataMember(Name = &quot;message&quot;)]
	public object Message { get; set; } // Changing this to string[] fails as well.
}

Demo fiddle #1 here.

This model doesn't really capture the fact that Message should be a string, or an array of strings, so you may instead prefer to use some surrogate property for serialization like so:

[DataContract]
public class Response
{
	[IgnoreDataMember]
	public string [] Messages { get; set; }
	
	[DataMember(Name = &quot;message&quot;)]
	object SerializedMessages 
	{ 
		get =&gt; Messages; 
		set =&gt; Messages = (value) switch
		{
			null =&gt; null,
			string s =&gt; new [] { s },
			string [] a =&gt; a,
			// Convert arrays of primitives to strings
			object [] a =&gt; a.Cast&lt;IFormattable&gt;().Select(f =&gt; Convert.ToString(f, CultureInfo.InvariantCulture)).ToArray(),
			_ =&gt; throw new ArgumentException(string.Format(&quot;Unknown value type {0}&quot;, value)),
		};
	}
}

Do note that, according to the docs

> For most scenarios that involve serializing to JSON and deserializing from JSON, we recommend the APIs in the System.Text.Json namespace.

Demo fiddle #2 here.

huangapple
  • 本文由 发表于 2023年6月15日 21:06:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76482804.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定