Сериализация данных в Custom DataSourceControl

Я пишу обычай DataSourceControl который в основном будет иметь возможность выбора, работающую как обычно ObjectDataSourceс TypeName а также SelectMethod свойства.

Данные, поступающие из класса TypeName, будут сохранены в файлах, проиндексированных хэшем значений параметров и ContextName. Это означает, что каждый раз, когда GridView запрашивает источник данных и задаются одинаковые значения параметров, элемент управления находит соответствующий файл и загружает данные оттуда. Фактически, каждая комбинация значений параметров будет генерировать новый файл с данными.

Эта функциональность может быть очень полезна в некоторых случаях, когда у нас слишком много времени, чтобы данные обрабатывались и извлекались из базы данных и не нуждались в обращении к пользователю (это всегда с последнего дня).

Основная сложность, с которой я сталкиваюсь, заключается в сериализации данных, поступающих с SelectMethod. Как единственное, что я знаю, это то, что возвращаемый тип будет экземпляром IEnumerable, я использую XMLSerializer для сохранения и извлечения содержимого данных из файла, но при попытке сериализации это выдает ошибку Cannot serialize interface,

Это основной код, который выполняет SelectMethod и выполняет часть сериализации:

//Gets the select type
Type selectType = Type.GetType(TypeName);

//Gets the select method
MethodInfo selectMethod = selectType.GetMethod(SelectMethod, BindingFlags.Instance | BindingFlags.Public);

//Creates a new instance of the TypeName class
object selectInstance = Activator.CreateInstance(selectType);

//Executes the select method
object selectResult = selectMethod.Invoke(selectInstance, parameters);
IEnumerable list = (IEnumerable)selectResult;

//Create a serializer
XmlSerializer serializer = new XmlSerializer(typeof(IEnumerable));

//Writes to the XML file
using (XmlWriter writer = XmlWriter.Create(filePath))
{
    serializer.Serialize(writer, list);
}

Я бы использовал этот код для десериализации:

//Creates the XML File
XmlSerializer deserializer = new XmlSerializer(typeof(IEnumerable));

IEnumerable list = null;

//Reads from the XML file
using (XmlReader writer = XmlReader.Create(filePath))
{
    list = (IEnumerable) deserializer.Deserialize(writer);
}

Как я могу обобщенно сериализировать / десериализовать результат выбора метода в XML?

Обновить:

Я пытался с помощью System.Web.UI.LosFormatter Сериализация / десериализация данных. Он сделал оба действия с экземпляром IEnumerable, но мне пришлось поставить Serializable атрибут на сущностях. Однако я заметил значительную разницу в производительности по сравнению с XMLSerializer при извлечении данных из файла. System.Web.UI.LosFormatter был в 4 раза медленнее при десериализации в моем конкретном тесте (файл 4 МБ). Файл данных будет в два раза меньше по сравнению с XMLSerializer Тхо. Итак, для меня, XMLSerializer по-прежнему лучший вариант.

Update2:

Пытался сделать простой тест, используя ServiceStack JsonSerializer со следующим кодом:

List<Dummy> dummies = new List<Dummy>();

dummies.Add(new Dummy() { Name = "name" });
dummies.Add(new Dummy() { Name = "name1" });
dummies.Add(new Dummy() { Name = "name2" });

IEnumerable enumerableThing = dummies;

string filePath = Path.Combine(Server.MapPath("~/App_Data"), "data2.json");
using (StreamWriter writer = new StreamWriter(filePath))
{
    JsonSerializer.SerializeToWriter<IEnumerable>(enumerableThing, writer);
}

using (StreamReader reader = new StreamReader(filePath))
{
    enumerableThing = JsonSerializer.DeserializeFromReader<IEnumerable>(reader);
}

Он работает сериализации, но при попытке DeserializeFromReader, он дает мне ошибку "Type System.Collections.IEnumerable is not of type IDictionary<,>", Есть ли способ, которым я мог бы сделать эту работу?

1 ответ

Ну, как вы уже обнаружили, вы не можете сериализовать интерфейс... который сказал, что вы могли бы обойти это и все еще быть "в неведении" о фактических типах объектов:

Во-первых, любой IEnumerable может быть преобразован в массив через Cast/ToArray комбо

var enumerableThing = Foo.GetEnumerable();    
var asArray = enumerableThing.Cast<object>().ToArray();

Во-вторых, массивы "известного типа" являются сериализуемыми

var allContainedTypes = asArray.Select(x => x.GetType()).Distinct().ToArray();
var ser = new XmlSerializer(asArray.GetType(), allContainedTypes);

var ser = new XmlSerializer(asArray.GetType());

var sb = new StringBuilder();
using(var sw = new StringWriter(sb))
using(var xw = XmlWriter.Create(sw))
    ser.Serialize(xw, asArray);

sb.ToString().Dump();

Или все вместе:

void Main()
{
    var enumerableThing = Foo.GetEnumerable();    
    var asArray = enumerableThing.Cast<object>().ToArray();

    var allContainedTypes = asArray.Select(x => x.GetType()).Distinct().ToArray();
    var ser = new XmlSerializer(asArray.GetType(), allContainedTypes);
    var sb = new StringBuilder();
    using(var sw = new StringWriter(sb))
    using(var xw = XmlWriter.Create(sw))
        ser.Serialize(xw, asArray);

    sb.ToString().Dump();
}


public class Foo
{
    public static IEnumerable GetEnumerable()
    {
        return new[] { 1, 2, 3, 4, 5, 6, 7 };
    }
    public static IEnumerable GetEnumerable2()
    {
        return new object[] { "1", 2, "bob", 4, null, 6, 7 };
    }
}

О, это производит формат:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfAnyType 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <anyType xsi:type="xsd:int">1</anyType>
    <anyType xsi:type="xsd:int">2</anyType>
    <anyType xsi:type="xsd:int">3</anyType>
    <anyType xsi:type="xsd:int">4</anyType>
    <anyType xsi:type="xsd:int">5</anyType>
    <anyType xsi:type="xsd:int">6</anyType>
    <anyType xsi:type="xsd:int">7</anyType>
</ArrayOfAnyType>

Редактировать: десериализация создает некоторые проблемы - однако, вы можете сделать что-то похожее на:

// Least common denominator...
object[] tempResult;

var assemblyStuffIsFrom = Assembly.GetExecutingAssembly();
var allSerializableTypes = assemblyStuffIsFrom
    .GetTypes()
    .Where(t => t.GetCustomAttributes(typeof(SerializableAttribute), true).Any())
    // TODO: add as much filtering as you can here to help trim down the set
    .ToArray();
var hope = new XmlSerializer(typeof(object[]), allSerializableTypes);
using(var sr = new StringReader(sb.ToString()))
using(var xr = XmlReader.Create(sr))
    tempResult = ((object[])hope.Deserialize(xr));
Другие вопросы по тегам