ICollection <t>为什么SerializationBinder是在HashSet <t>上调用而不是在List <t>上调用

时间:2015-08-19 15:45:25

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

我有一个这样的对象:

public class MyCollec
{
    public ICollection<MyObject> MyCollection { get; set; }

    public MyCollec()
    {
        MyCollection = new List<MyObject>();
    }
}

在WebApi中,我的webApiConfig中有一个SerializationBinder:

jsonFormatter.SerializerSettings.Binder = new DefaultSerializationBinder();

当我调用我的控制器返回我的所有对象时,我得到一个像这样的结构

{
  "MyCollection": [
    {
        ...
    },
    {
        ...
    }
  ]
}

但是如果我用hashset改变我的构造函数

public MyCollec()
{
    MyCollection = new HashSet<MyObject>();
}

我的控制器的结果是这样的:

{
  "MyCollection": {
    "$type": "System.Collections.Generic.HashSet`1[[WebApplication1.Models.MyObject, WebApplication1]], System.Core",
    "$values": [
            {
                ...
            },
            {
                ...
            }
        ]
    }
}

我想明白为什么?导致这种行为的区别是什么?

是否可以使用HashSet但是给出与列表相同的结果?

编辑:

@Grundy评论:

  

TypeNameHandling属性为Auto,来自doc:包含.NET类型   当被序列化的对象的类型不相同时的名称   它的声明类型。所以似乎HashSet序列化与它不一样   声明的类型。

这是真的,这是原因,但为什么它不适用于List?类型与ICollection不同,如HashSet ......

此外,我尝试更改TypeNameHandling属性,但只有在继承的对象上没有set $ type选项...

http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm

1 个答案:

答案 0 :(得分:0)

感谢@Grundy的线索。

我保持

jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

我有一个自定义的JsonConverter来改变HashSet的序列化

public class HashSetArrayConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        IEnumerable collectionObj = (IEnumerable)value;

        writer.WriteStartObject();

        foreach (object currObj in collectionObj)
        {
            serializer.Serialize(writer, currObj);
        }

        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize(reader, objectType);
    }

    public override bool CanConvert(Type objectType)
    {
        var canConvert = IsAssignableToGenericType(objectType, typeof(HashSet<>));
        return canConvert;
    }

    private static bool IsAssignableToGenericType(Type givenType, Type genericType)
    {
        var interfaceTypes = givenType.GetInterfaces();

        if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
        {
            return true;
        }

        if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
            return true;

        Type baseType = givenType.BaseType;
        if (baseType == null) return false;

        return IsAssignableToGenericType(baseType, genericType);
    }
}

并将其添加到config:

jsonFormatter.SerializerSettings.Converters.Add(new HashSetArrayConverter());