动态对象序列化

发布于 2024-09-06 13:44:11 字数 2746 浏览 5 评论 0 原文

我尝试使用 BinaryFormatter 序列化 DynamicObject 类,但是:

  • 输出文件太大,不完全适合线路
  • 循环引用未处理(序列化时卡住)

序列化一个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,但我不太确定如何在这样的上下文中使用它(我什至不确定我从存储库中选择了正确的版本,但是嘿)。

我知道这更多的是代码而不是文字,但我认为我无法更好地解释这个场景。请告诉我是否可以添加一些内容以使这个问题更清楚。

非常感谢任何帮助。

I tried to serialize a DynamicObject class with BinaryFormatter, but:

  • Output file is too big, not exactly wire-friendly
  • Circular References not handled (stuck while serializing)

Since serializing a DynamicObject means very little by itself, here's the class I tried to serialize:

[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);                 
        }
    }

}

(I guess I could have used an ExpandoObject, but that's another story.)

Here's a simple test program:

    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();

    }

Putting some breakpoints here and there helped me have a look at obj2 contents and it looks like the original data is correctly deserialized, though with the above shortcomings if you get imaginative and move data around.

I had a look at Marc Gravell's protobuf-net, but I'm not really sure how to use it in such a context (I'm not even sure I picked up the right version from the repository, but hey).

I know it's more code than words, but I don't think I can explain the scenario any better. Please do tell if there's something I can add to make this question clearer.

Any help is much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

七婞 2024-09-13 13:44:11

我 98% 确信该序列适用于动态对象。

  1. 将对象转换为 Expando 对象
  2. 将 Expando 对象转换为字典类型
  3. 使用 ProtoBuf-net Serializer.Serialize / .Deserialize 按照正常操作
  4. 将字典转换为 Expando 对象

您可以将对象转换为名称/值对的集合以进行传输。

这只是动态功能的一小部分,但也许对您来说已经足够了。

有一些自定义代码可以处理上面的一些转换,如果有兴趣的话我可以向您展示。

当动态是类的占位符时,我没有解决方案。对于这种情况,我建议获取类型并使用 switch 语句根据需要进行序列化/反序列化。对于最后一种情况,您需要放置一些内容来指示您需要哪种类型的通用反序列化(字符串/id/完全限定类型名称/等)。假设您正在处理预期类型的​​列表。

注意:Expando 实现 IDictionary。 Expando 只是一个键/值对列表。 IE。你点的东西是键,值是实现它的任何函数链的返回值。有一组动态界面用于自定义语法糖体验,但大多数时候您不会看它们。

refs:

I'm 98% certain that this sequence will work for a dynamic object.

  1. convert object to an Expando Object
  2. cast expando object to be of type Dictionary
  3. use ProtoBuf-net Serializer.Serialize / .Deserialize as per normal
  4. convert dictionary to Expando Object

You can convert objects to a collection of name/value pairs for transfering.

That's just a small subset of what dynamic can do, but perhaps it is enough for you.

There's some custom code to handle some of the conversions above that I can show you if there's interest.

I don't have a solution for when dynamic is a placeholder to a class. For this case I'd suggest getting the type and using a switch statement to serialize / deserialize as you require. For this last case, you'd need to place a something to indicate which type of generic deserialization that you need (string / id / fully qualified type name / etc). Assumption is that you are dealing with a list of expected types.

Note: Expando implements IDictionary. An Expando is merely merely a list of key/value pairs. ie. the thing you dot into is the key, and the value is the return from whatever chain of functions implements that. There are a set of dynamic interfaces for customising the syntactic sugar experience, but most of the time you wont to look at them.

refs:

尸血腥色 2024-09-13 13:44:11

我不确定 JSON 在您的环境中是否可以接受,但如果是,我已经使用了 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");
    }

I am not sure if JSON would be acceptable in your senario, but if it is I have used Json.net (http://json.codeplex.com) to serialize a dynamic types. It works quite well, it is fast, and the output is small in size. While Json.net doesn't return dynamic objects directly, it is very easy to convert the deserialized output of Json.Net to any dynamic type. In the example below I am using ExpandoObject as my dynamic type. The code below is what I have used in the Facebook Graph Toolkit. See this link for the original source: 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");
    }
时光瘦了 2024-09-13 13:44:11

首先,文件的大小取决于两件事(如果我理解 BinaryFormatter 的工作原理,如果我错了,请纠正我):

  1. 正在序列化的实际值的大小,以及
  2. 用于序列化对象值的名称SerializationInfo.AddValue 方法,该方法存储在输出文件中,以便在反序列化期间可以使用同名的值。

显然,#1 会导致最大的速度减慢,只能通过优化您尝试序列化的对象来减少速度。

因为您使用的是动态对象,所以#2 引起的几乎不明显的小尺寸增加是不可避免的。如果您提前知道对象成员的类型和名称,则可以在迭代时为对象的每个成员指定非常短的、按顺序确定的名称(“1”、“2”、“3”等)覆盖对象的成员,通过 SerializationInfo.AddValue 添加它们。然后,在反序列化期间,您可以使用具有相同顺序确定名称的 SerializationInfo.GetValue ,并且只要您迭代,无论被反序列化的值的实际名称是什么,反序列化都会正常工作对象的成员按照添加的顺序排列。当然,这可能只能为每个成员平均节省 4 或 5 个字节,但这些少量的内容加起来就可以形成大对象。

@Raine:(我想我可以使用 ExpandoObject,但那是另一个故事了。)

并非如此;我更改了您的代码示例以使用 ExpandoObject 而不是您的 Entity 类,并向我抛出了 SerializationExceptionExpandoObject 未使用 SerializedAttribute 进行标记,并且它没有用于反序列化或序列化的适当构造函数。但是,这并不意味着如果您确实想要的话,您不能使用 ExpandoObject:它实现了 IDictionary,后者又实现了 ICollection< ;KeyValuePair<字符串,对象>>。因此,ExpandoObject 实例是 KeyValuePair 实例的集合,这些实例标记为可序列化。因此,您可以序列化 ExpandoObject,但必须将其转换为 ICollection> 并序列化每个 KeyValuePair分别在其中。不过,就优化原始代码示例而言,这毫无意义,因为它占用了同样多的文件空间。

总之,我真的不认为有任何方法可以优化动态对象的序列化 - 每次序列化时都必须循环遍历对象的成员,并且您无法事先知道对象的大小(根据动态的定义) 。

First off, the size of your file depends on 2 things (if I understand how BinaryFormatter works, please correct me if I'm wrong):

  1. The size of the actual values being serialized, and
  2. The names used to serialize the object's values with the SerializationInfo.AddValue method, which are stored in the output file so values can be used during deserialization with the same name.

Obviously, #1 is going to cause your biggest slowdown, which can only be reduced by optimizing the objects you're trying to serialize.

Because you're using dynamic objects, the almost unnoticably small size increase caused by #2 is unavoidable. If you knew the types and names of the object's members ahead of time, you could just give each member of the object very short, sequentially-determined name ("1", "2", "3", etc.) as you iterated over the object's members, adding them via SerializationInfo.AddValue. Then, during deserialization, you could use SerializationInfo.GetValue with the same sequentially-determined name, and deserialization would work just fine, regardless of the actual names of the values being deserialized, as long as you iterated through the object's members in the same order they were added in. Granted, this might only save you an average of 4 or 5 bytes per member, but those little amounts can add up in large objects.

@Raine: (I guess I could have used an ExpandoObject, but that's another story.)

Not so; I changed your code sample to use ExpandoObject instead of your Entity class, and got a SerializationException thrown at me. ExpandoObject is not marked with a SerializableAttribute, and it doesn't have the appropriate constructors to be deserialized or serialized. However, this doesn't mean you can't use ExpandoObject if you really want to: it implements IDictionary<string, object>, which in turn implements ICollection<KeyValuePair<string, object>>. Thus, an ExpandoObject instance is a collection of KeyValuePair<string, object> instances, which are marked as serializable. So, you could serialize an ExpandoObject, but you'd have to cast it as ICollection<KeyValuePair<string, object>> and serialize each KeyValuePair<string, object> in it individually. This would pointless, though, in terms of optimizing your original code sample, because it takes just as much file space.

In summary, I really don't think there's any way you could optimize serializing a dynamic object- you have to loop through the object's members every time it's serialized, and you have no way to know the object's size beforehand (by definition of dynamic).

迷爱 2024-09-13 13:44:11

我不知道 SharpSerializer 是否支持动态对象,但可能值得一试:

http://www .sharpserializer.com/en/index.html

I don't know if SharpSerializer supports Dynamic Objects but it might be worth a try:

http://www.sharpserializer.com/en/index.html

场罚期间 2024-09-13 13:44:11

序列化动态对象的一个​​建议是将它们转换为 String 然后序列化,然后您可以反序列化回您的对象(如果适用于您的情况)。

One Suggestion for serializing dynamic object is convert them to String and then serialize, you can then deserialize back to your object, if applicable in your case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文