使用 FromBody 在 WebAPI 中建模的 JSON 对象数组
Posted
技术标签:
【中文标题】使用 FromBody 在 WebAPI 中建模的 JSON 对象数组【英文标题】:JSON Array of Objects to Model in WebAPI using FromBody 【发布时间】:2015-01-19 22:12:12 【问题描述】:我正在创建一个 Web Api 方法,它应该通过 XML 或 JSON 接受对象列表并将它们添加到数据库中。
这是我目前拥有的一个非常基本的版本:
[HttpPost]
public HttpResponseMessage Put([FromBody]ProductAdd productAdd)
//do stuff with productadd object
return Request.CreateResponse(HttpStatusCode.OK);
它接受的对象列表的模型结构如下:
public class ProductAdd
public List<ProductInformation> Products get; set;
public class ProductInformation
public string ProductName get; set;
当我使用 XML - (Content-Type: application/xml)
<?xml version="1.0" encoding="utf-8"?>
<ProductAdd>
<Products>
<ProductInformation>
<ProductName>Seahorse Necklace</ProductName>
</ProductInformation>
</Products>
<Products>
<ProductInformation>
<ProductName>Ping Pong Necklace</ProductName>
</ProductInformation>
</Products>
</ProductAdd>
但是当我尝试使用 JSON(内容类型:application/json)提供相同的东西时,产品列表为空
"ProductAdd":
"Products": [
"ProductInformation": "ProductName": "Seahorse Necklace"
,
"ProductInformation": "ProductName": "Ping Pong Necklace"
]
当另一个对象中有一个对象数组时,JSON 序列化程序是否存在问题?
有什么办法可以解决这个问题吗?
谢谢
编辑: 您为 XML 和 Json 使用了哪些序列化程序? XML:XmlSerializer JSON:牛顿软件
【问题讨论】:
【参考方案1】:您发送到 Web API 方法的 JSON 与您反序列化到的结构不匹配。与 XML 不同,JSON 中的根对象没有名称。您需要从 JSON 中删除包装器对象才能使其工作:
"Products": [
"ProductInformation": "ProductName": "Seahorse Necklace"
,
"ProductInformation": "ProductName": "Ping Pong Necklace"
]
或者,您可以更改您的类结构以添加一个包装类,但是您还需要更改您的 XML 以匹配它。
public class RootObject
public ProductAdd ProductAdd get; set;
【讨论】:
【参考方案2】:在反序列化莫名其妙地失败的情况下,我发现序列化测试对象并将实际输出与所需输入进行比较很有帮助。如果输出与期望的输入不同,这可能是错误的原因。在您的情况下,如果我反序列化 XML 并将其重新序列化为 JSON,我会得到:
"Products": [
"ProductName": "Seahorse Necklace"
,
"ProductName": "Ping Pong Necklace"
]
如您所见,与重新序列化的 XML 相比,您的 JSON 中有两个额外的间接级别:有一个根对象名称,它不应该出现在 JSON 中,还有一个集合元素类型名称,它也不应该出现。 (不过,这两者都是 XML 中的共同特征。)
是否可以更改您的 JSON,使其在间接情况下不具有这些额外级别? (例如,此 JSON 是否通过脚本从 XML 转换为测试目的,因此不能反映实际需求?)如果是这样,那么您的问题就解决了。
如果没有,那么这里有一些反序列化的选项:
要使用 JSON 读取和写入根对象名称,请参阅解决方案 here 或 here
或者,滚动您自己的代理包装器:
public sealed class ProductAddWrapper
public ProductAddWrapper()
public ProductAddWrapper(ProductAdd product)
this.ProductAdd = product;
public ProductAdd ProductAdd get; set;
public static implicit operator ProductAdd(ProductAddWrapper wrapper) return wrapper.ProductAdd;
public static implicit operator ProductAddWrapper(ProductAdd product) return new ProductAddWrapper(product);
在列表中注入额外的间接级别有点困难。您需要做的是在读取和写入 JSON 时动态重构 JSON,添加或删除额外的人工嵌套级别。这可以通过JsonConverter
来完成:
class CollectionWithNamedElementsConverter : JsonConverter
static Type GetEnumerableType(Type type)
foreach (Type intType in type.GetInterfaces())
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
return intType.GetGenericArguments()[0];
return null;
public override bool CanConvert(Type objectType)
return typeof(IEnumerable).IsAssignableFrom(objectType)
&& !typeof(string).IsAssignableFrom(objectType)
&& GetEnumerableType(objectType) != null;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
JArray originalArray = JArray.Load(reader);
if (originalArray == null)
return null;
JArray array = new JArray();
foreach (var item in originalArray)
var child = item.Children<JProperty>().FirstOrDefault();
if (child != null)
var value = child.Value;
array.Add(child.Value);
return serializer.Deserialize(new StringReader(array.ToString()), objectType);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var objectType = value.GetType();
var itemType = GetEnumerableType(objectType);
IEnumerable collection = value as IEnumerable;
writer.WriteStartArray();
foreach (object item in collection)
writer.WriteStartObject();
writer.WritePropertyName(itemType.Name);
serializer.Serialize(writer, item);
writer.WriteEndObject();
writer.WriteEndArray();
然后通过将[JsonConverter(typeof(CollectionWithNamedElementsConverter))]
属性应用于具有项目类型名称的集合来使用它:
public class ProductAdd
[JsonConverter(typeof(CollectionWithNamedElementsConverter))]
public List<ProductInformation> Products get; set;
【讨论】:
以上是关于使用 FromBody 在 WebAPI 中建模的 JSON 对象数组的主要内容,如果未能解决你的问题,请参考以下文章
使用 HttpClient 和 Web API 方法 [FromBody] 参数发布到 Web API 最终为空
WebAPI 2不反序列化List POST请求中FromBody对象的属性