【问题标题】:How to XML serialize an ExpandoObject into property/value pairs?如何 XML 将 ExpandoObject 序列化为属性/值对?
【发布时间】:2015-07-14 12:23:44
【问题描述】:

我在调用 OnActionExecuted 时拦截了一个 Web API 2 管道。在这里,我将操作返回的对象递归地转换为 ExpandoObject(即对象上本身是对象的任何属性也会转换为 ExpandoObjects,依此类推)。

它 XML 序列化 OK,但仅作为字典(可能是因为 ExpandoObject 实现了 IDictionary,它只是从中提取键和值)。我宁愿看到它序列化,就好像它是一个具有属性的对象,而不是一堆键/值对。

有什么方法可以在不编写我自己的 XML 序列化程序的情况下做到这一点?

【问题讨论】:

    标签: .net xml serialization xml-serialization expandoobject


    【解决方案1】:

    您可以将ExpandoObject 包装在ISerializable 实现中。它递归地包装包含ExpandoObject

    [Serializable]
    public class SerializableWrapper : ISerializable
    {
        private IDictionary<string, object> _data;
    
        public IDictionary<string, object> Data
        {
            get { return _data; }
        }
    
        public SerializableWrapper(IDictionary<string, object> data)
        {
            _data = data;
        }
    
        protected SerializableWrapper(SerializationInfo info, StreamingContext context)
        {
            this._data = new Dictionary<string, object>();
            var enumerator = info.GetEnumerator();
            while (enumerator.MoveNext())
            {
                this._data[enumerator.Name] = enumerator.Value;
            }
        }
    
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {            
            foreach (var kvp in this._data)
            {
                info.AddValue(kvp.Key, Wrap(kvp.Value));
            }
        }
    
        private static object Wrap(object value)
        {
            var expando = value as ExpandoObject;
            if (expando != null)
            {
                return new SerializableWrapper(expando);
            }
            var expandoList = value as IEnumerable<ExpandoObject>;
            if (expandoList != null)
            {
                return expandoList
                    .Select(Wrap)
                    .Cast<SerializableWrapper>()
                    .ToArray();
            }
            var list = value as IEnumerable;
            if (list != null && !(value is string))
            {
                return list
                    .Cast<object>()
                    .Select(Wrap)
                    .ToArray();
            }
            return value;
        }
    }
    
    dynamic obj = new ExpandoObject();
    obj.Foo = 3;
    obj.Bar = new [] { new ExpandoObject() };
    obj.Bar[0].Baz = "Qux";
    
    var wrapped = new SerializableWrapper(obj);
    
    var ser = new DataContractSerializer(typeof(SerializableWrapper), new [] { typeof(SerializableWrapper[]), typeof(object[]) });
    var mem = new MemoryStream();
    ser.WriteObject(mem, wrapped);
    

    生成:

    <SerializableWrapper xmlns="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
      <Foo i:type="x:int" xmlns="">3</Foo>
      <Bar i:type="a:ArrayOfSerializableWrapper" xmlns="" xmlns:a="http://schemas.datacontract.org/2004/07/">
        <a:SerializableWrapper>
          <Baz i:type="x:string">Qux</Baz>
        </a:SerializableWrapper>
      </Bar>
    </SerializableWrapper>
    

    序列化的 XML 并不漂亮。您可以使用DataContractResolver 或对 XML 进行后处理以使其不那么难看。

    要再次反序列化,可以使用

    mem.Position = 0;
    var deserialized = (SerializableWrapper) ser.ReadObject(mem);
    

    另一种方法是实现IXmlSerializable 并改用XmlSerializer

    【讨论】:

    • OP 询问了 XML 序列化。
    • 如何反序列化它?
    • @drowa 我之前忘记了反序列化构造函数。我现在加了。
    猜你喜欢
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 2013-01-20
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多