使用FromBody在Web API中建模的JSON对象数组

时间:2014-11-21 17:20:13

标签: c# xml json asp.net-web-api

我正在创建一个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>

Products has 2 Items

但是当我尝试使用JSON(Content-Type:application / json)提供同样的东西时,Products列表为空

{
  "ProductAdd": {
    "Products": [
      {
        "ProductInformation": { "ProductName": "Seahorse Necklace" }
      },
      {
        "ProductInformation": { "ProductName": "Ping Pong Necklace" }
      }
    ]
  }
}

Products is null

当另一个对象中有一个对象数组时,JSON序列化程序是否存在问题?

有关解决这个问题的任何想法?

由于

编辑: 您使用哪些序列化程序用于XML和Json? XML:XmlSerializer JSON:Newtonsoft

2 个答案:

答案 0 :(得分:4)

您发送到Web API方法的JSON与您要反序列化的结构不匹配。与XML不同,JSON中的根对象没有名称。您需要从JSON中删除包装器对象才能使其工作:

  {
    "Products": [
      {
        "ProductInformation": { "ProductName": "Seahorse Necklace" }
      },
      {
        "ProductInformation": { "ProductName": "Ping Pong Necklace" }
      }
    ]
  }

或者,您可以将类结构更改为ADD包装类,但是您还需要更改XML以匹配它。

public class RootObject
{
    public ProductAdd ProductAdd { get; set; }
}

答案 1 :(得分:3)

在反序列化神秘失败的情况下,我发现序列化测试对象并将实际输出与所需输入进行比较是有帮助的。如果输出与所需输入不同,那可能是导致错误的原因。在您的情况下,如果我反序列化XML并将其重新序列化为JSON,我得到:

{
  "Products": [
    {
      "ProductName": "Seahorse Necklace"
    },
    {
      "ProductName": "Ping Pong Necklace"
    }
  ]
}

正如您所看到的,与重新序列化的XML相比,JSON中有两个额外的间接级别:有一个根对象名称,它不应该出现在JSON中,并且还有集合元素类型名称,这也是不应该在场。 (但这两者都是XML中的常见功能。)

是否可以更改您的JSON,以便在间接时它没有这些额外的级别? (例如,这个JSON是通过脚本从XML转换出来进行测试的,因此不能反映实际需求吗?)如果是这样,那么你的问题就解决了。

如果没有,那么这里有一些反序列化的选项:

  1. 阅读&amp;使用JSON编写根对象名称,请参阅解决方案herehere

    或者,滚动您自己的代理包装器:

    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); }
    }
    
  2. 在列表中注入额外的间接级别有点困难。您需要做的是在读取和写入时动态重构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();
        }
    }
    
  3. 然后通过将[JsonConverter(typeof(CollectionWithNamedElementsConverter))]属性应用于具有项类型名称的集合来使用它:

        public class ProductAdd
        {
            [JsonConverter(typeof(CollectionWithNamedElementsConverter))]
            public List<ProductInformation> Products { get; set; }
        }