Сериализатор DataContract не работает для подобных типов

У меня есть приложение, в котором мне нужно использовать сериализатор контракта данных для разных, но похожих типов:

  • Общие списки
  • Общие коллекции
  • Массивы

По запросу от Scrobi, вот полный пример модели:

[DataContract]
public class Model
{
    public Model()
    {
        List = new List<int>(new[] {1, 2, 3});
        Collection = new Collection<int>(new[] {4, 5, 6});
        Array = new[] {7, 8, 9};
    }

    [DataMember]
    public object List { get; set; }

    [DataMember]
    public object Collection { get; set; }

    [DataMember]
    public object Array { get; set; }

    public string SerializeMe()
    {
        var serializer = new DataContractSerializer(typeof(Model), GetKnownTypes());

        using (var stream = new MemoryStream())
        {
            serializer.WriteObject(stream, this); // exception

            return Encoding.UTF8.GetString(stream.GetBuffer());
        }
    }

    public static Type[] GetKnownTypes()
    {
        return new[]
        {
            typeof(List<int>),
            typeof(Collection<int>), // error
            typeof(int[]) // error
        };
    }
}

Проблема в том, что я не могу добавить общий список, коллекцию и массив одновременно, потому что все они используют один и тот же контракт данных.

Когда я использую только один из типов коллекций, я не могу сериализовать остальные, потому что они неизвестны (контракт данных присутствует, но для другого типа). И да, поля должны быть объектами, чтобы это работало в моем случае (и в реальном приложении я не могу добавлять к ним атрибуты).

В приложении, которое я пишу, очень непрактично использовать только один из типов, поскольку это своего рода среда разработки, и разработчик может свободно выбирать тип.

Есть ли способ обойти это ограничение сериализатора контрактов данных?

Спасибо,


person knurd nerd    schedule 03.07.2017    source источник
comment
Конечно. Но вы все еще не можете сериализовать список, когда вы добавили коллекцию в Известные типы.   -  person knurd nerd    schedule 03.07.2017
comment
Возможно, ваш пример был упрощен, но зачем вам добавлять Collection<int> к известным типам? Если все коллекции уже эквивалентны и взаимозаменяемы?   -  person Scrobi    schedule 03.07.2017
comment
Потому что, когда вы добавляете Collection‹int› и пытаетесь сериализовать List‹int›, это не работает.   -  person knurd nerd    schedule 03.07.2017
comment
Я думаю, было бы полезно, если бы вы добавили свои модели, где вы используете разные коллекции.   -  person Scrobi    schedule 03.07.2017
comment
Спасибо за внимание. Я добавил полный пример.   -  person knurd nerd    schedule 03.07.2017
comment
Вот аналогичная проблема с XmlSerializer: XmlInclude: список и массив. Вероятно, подобное решение сработает и здесь.   -  person dbc    schedule 03.07.2017
comment
Вы написали, а в реальном приложении я не могу добавить к ним атрибуты. Означает ли это, что вы вообще не можете изменить сериализуемые типы?   -  person dbc    schedule 04.07.2017


Ответы (1)


У меня есть частичный ответ, но по какой-то причине при тестировании Collection<int> выдает ошибку при десериализации. Может быть, вы можете работать с этим, чтобы найти полное решение.

Я создал DataContractResolver, позволяющий переопределить xsi:type. Существует некоторая документация здесь

public class MyResolver : DataContractResolver
{
    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        string name = string.Empty;
        bool isFound = false;
        if (type.Name == "Int32[]")
        {
            name = "IntArray";
            isFound = true;
        }
        if (type.Name.Contains("List") && type.FullName.Contains("Int")) //find List<int>
        {
            name = "IntList";
            isFound = true;
        }
        if (type.Name.Contains("Collection") && type.FullName.Contains("Int")) //find Collection<int> 
        {
            name = "IntCollection";
            isFound = true;
        }

        if (isFound)
        {
            XmlDictionary dictionary = new XmlDictionary();
            typeName = dictionary.Add(name);
            typeNamespace = dictionary.Add("http://tempuri.com");
            return true;
        }

        return knownTypeResolver.TryResolveType(type, declaredType, knownTypeResolver, out typeName, out typeNamespace);
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        if (typeName == "IntArray" )
        {
            return typeof(int[]);
        }
        if (typeName == "IntList")
        {
            return typeof(List<int>);
        }
        if (typeName == "IntCollection")
        {
            return typeof(Collection<int>);
        }


        return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
    }
}

Тогда вам не нужен ваш GetKnownTypes() и создайте DataContractSerializer следующим образом:

var serializer = new DataContractSerializer(typeof(Model),null, int.MaxValue, false, false,null, new MyResolver());

Надеюсь, это немного поможет.

person Scrobi    schedule 04.07.2017
comment
Здорово! Это похоже на то, что я искал, и, безусловно, дает мне достаточно указателей в правильном направлении. Спасибо большое :-) - person knurd nerd; 04.07.2017