Сериализация динамического объекта
Я попытался сериализовать класс DynamicObject
с помощью BinaryFormatter
, но:
- Выходной файл слишком большой, не совсем дружественный к проводке.
- Циркулярные ссылки не обрабатываются (застревают при сериализации)
Поскольку сериализация DynamicObject
означает очень мало сама по себе, здесь класс, который я попытался сделать сериализацией:
[Serializable()]
class Entity
: DynamicObject, ISerializable
{
IDictionary<string, object> values = new Dictionary<string, object>();
public Entity()
{
}
protected Entity(SerializationInfo info, StreamingContext ctx)
{
string fieldName = string.Empty;
object fieldValue = null;
foreach (var field in info)
{
fieldName = field.Name;
fieldValue = field.Value;
if (string.IsNullOrWhiteSpace(fieldName))
continue;
if (fieldValue == null)
continue;
this.values.Add(fieldName, fieldValue);
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
this.values.TryGetValue(binder.Name, out result);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.values[binder.Name] = value;
return true;
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (var kvp in this.values)
{
info.AddValue(kvp.Key, kvp.Value);
}
}
}
(Думаю, я мог бы использовать ExpandoObject, но это другая история.)
Здесь простая тестовая программа:
static void Main(string[] args)
{
BinaryFormatter binFmt = new BinaryFormatter();
dynamic obj = new Entity();
dynamic subObj = new Entity();
dynamic obj2 = null;
obj.Value = 100;
obj.Dictionary = new Dictionary<string, int>() { { "la la la", 1000 } };
subObj.Value = 200;
subObj.Name = "SubObject";
obj.Child = subObj;
using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
{
binFmt.Serialize(stream, obj);
}
using (var stream = new FileStream("test.txt", FileMode.Open))
{
try
{
obj2 = binFmt.Deserialize(stream);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Console.ReadLine();
}
Помещение некоторых точек останова здесь и там помогло мне взглянуть на содержимое obj2, и похоже, что исходные данные правильно десериализованы, хотя с вышеуказанными недостатками, если вы получаете воображение и перемещаете данные.
Я посмотрел на Marc Gravell protobuf-net, но я не уверен, как использовать его в таком контексте (я даже не уверен, что взял правильную версию из репозитория, но эй).
Я знаю, что это больше кода, чем слов, но я не думаю, что лучше объясню сценарий. Пожалуйста, скажите, есть ли что-то, что я могу добавить, чтобы сделать этот вопрос более ясным.
Любая помощь очень ценится.
Ответы
Ответ 1
Я на 98% уверен, что эта последовательность будет работать для динамического объекта.
- преобразовать объект в объект Expando
- листинг объекта expando типа словаря
- используйте ProtoBuf-net Serializer.Serialize/.Deserialize в соответствии с нормальным
- конвертировать словарь в объект Expando
Вы можете конвертировать объекты в коллекцию пар имя/значение для передачи.
Это всего лишь небольшое подмножество того, что может сделать динамика, но, возможно, этого вам достаточно.
Там какой-то пользовательский код для обработки некоторых вышеперечисленных преобразований, которые я могу показать вам, если есть интерес.
У меня нет решения, когда динамика является заполнителем класса. В этом случае я предлагаю получить тип и использовать оператор switch для сериализации/десериализации по мере необходимости. В этом последнем случае вам нужно поместить что-то, чтобы указать, какой тип общей десериализации вам нужен (строка/id/полное имя типа /etc ). Предполагается, что вы имеете дело со списком ожидаемых типов.
Примечание. Expando реализует IDictionary. Expando представляет собой просто список пар ключ/значение. то есть. то, что вы вставляете в это ключ, и значение - это возврат из любой цепочки функций. Есть набор динамических интерфейсов для настройки синтаксического опыта сахара, но большую часть времени вы привыкли смотреть на них.
рефов:
Ответ 2
Я не уверен, что JSON будет приемлемым в вашем senario, но если это я использовал Json.net(http://json.codeplex.com) для сериализации динамических типов. Он работает довольно хорошо, он работает быстро, а выход небольшой по размеру. Хотя Json.net не возвращает динамические объекты напрямую, очень легко преобразовать десериализованный вывод Json.Net в любой динамический тип. В приведенном ниже примере я использую ExpandoObject как мой динамический тип. Код ниже - это то, что я использовал в Facebook Graph Toolkit. Посмотрите эту ссылку для исходного источника: http://facebookgraphtoolkit.codeplex.com/SourceControl/changeset/view/48442#904504
public static dynamic Convert(string s) {
object obj = Newtonsoft.Json.JsonConvert.DeserializeObject(s);
if (obj is string) {
return obj as string;
} else {
return ConvertJson((JToken)obj);
}
}
private static dynamic ConvertJson(JToken token) {
// FROM : http://blog.petegoo.com/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx
// Ideally in the future Json.Net will support dynamic and this can be eliminated.
if (token is JValue) {
return ((JValue)token).Value;
} else if (token is JObject) {
ExpandoObject expando = new ExpandoObject();
(from childToken in ((JToken)token) where childToken is JProperty select childToken as JProperty).ToList().ForEach(property => {
((IDictionary<string, object>)expando).Add(property.Name, ConvertJson(property.Value));
});
return expando;
} else if (token is JArray) {
List<ExpandoObject> items = new List<ExpandoObject>();
foreach (JToken arrayItem in ((JArray)token)) {
items.Add(ConvertJson(arrayItem));
}
return items;
}
throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token");
}
Ответ 3
Во-первых, размер вашего файла зависит от двух вещей (если я понимаю, как работает BinaryFormatter, пожалуйста, исправьте меня, если я ошибаюсь):
- Размер сериализуемых фактических значений и
- Имена, используемые для сериализации значений объекта с помощью метода
SerializationInfo.AddValue
, которые хранятся в выходном файле, поэтому значения могут использоваться во время десериализации с тем же именем.
Очевидно, что # 1 приведет к вашему самому большому замедлению, которое может быть уменьшено только путем оптимизации объектов, которые вы пытаетесь сериализовать.
Поскольку вы используете динамические объекты, почти незаметно небольшое увеличение размера, вызванное # 2, неизбежно. Если вы знали типы и имена участников объекта раньше времени, вы могли бы просто дать каждому члену объекта очень короткое, последовательно определяемое имя ( "1", "2", "3" и т.д.) По мере повторения над объектами, добавив их через SerializationInfo.AddValue
. Затем во время десериализации вы можете использовать SerializationInfo.GetValue
с тем же именем, что и в последовательности, и десериализация будет работать отлично, независимо от фактических имен десериализуемых значений, если вы повторяете элементы объекта в том же порядке они добавлены. Конечно, это может сэкономить вам в среднем 4 или 5 байт на одного члена, но эти небольшие суммы могут складываться в больших объектах.
@Raine: (Думаю, я мог бы использовать ExpandoObject, но это другая история.)
Не так; Я изменил ваш пример кода, чтобы использовать ExpandoObject
вместо вашего класса Entity
, и получил SerializationException
, брошенный на меня. ExpandoObject
не помечен SerializableAttribute
, и он не имеет десериализации или сериализации соответствующих конструкторов. Однако это не означает, что вы не можете использовать ExpandoObject, если вы действительно хотите: он реализует IDictionary<string, object>
, который, в свою очередь, реализует ICollection<KeyValuePair<string, object>>
. Таким образом, экземпляр ExpandoObject
представляет собой набор экземпляров KeyValuePair<string, object>
, которые помечены как сериализуемые. Таким образом, вы можете сериализовать ExpandoObject, но вам нужно будет указать его как ICollection<KeyValuePair<string, object>>
и упорядочить каждый KeyValuePair<string, object>
в нем по отдельности. Однако это было бы бессмысленно с точки зрения оптимизации исходного образца кода, поскольку оно занимает столько же места в файле.
В целом, я действительно не думаю, что вы могли бы оптимизировать сериализацию динамического объекта - вам нужно циклически проходить через членов объекта каждый раз, когда он сериализуется, и вы не можете заранее знать размер объекта (по определению динамического).
Ответ 4
Я не знаю, поддерживает ли SharpSerializer динамические объекты, но может быть стоит попробовать:
http://www.sharpserializer.com/en/index.html