Сериализация данных в 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));