如何将对象集合/字典序列化为<key> value </key>

时间:2012-09-14 09:43:29

标签: c# .net xml serialization xml-serialization

有没有办法将键/值对(最好是强类型,但也可能来自词典)序列化为下面所需的格式?

public List<Identifier> Identifiers = new List<Identifiers>();

public class Identifier
{
    public string Name { get; set; }
    public string Description { get; set; }
}

这通常会序列化为以下内容:

<Identifiers>
  <Identifier>
    <Name>somename</Name>
    <Description>somedescription</Description>
  </Identifier>
  <Identifier>
    ...
  </Identifier>
</Identifiers>

我们考虑的另一种可能方法是使用哈希表/字典:

public Dictionary<string, string> Identifiers = new Dictionary<string,string>
{
    { "somename", "somedescription"},
    { "anothername", "anotherdescription" }
};

但这需要自定义序列化词典或自定义XmlWriter

我们希望实现的输出是:

<Identifiers>
  <somename>somedescription</somename>
  <anothername>anotherdescription</anothername>
</Identifiers>

因此,我们正在寻找代码示例,以了解如何以最佳方式获取我们想要的输出。

编辑:也许我应该更好地解释一下。我们已经知道如何序列化对象。我们正在寻找的是特定类型序列化的答案......我将扩展上面的问题

9 个答案:

答案 0 :(得分:22)

使用LINQ to XML

很容易
Dictionary<string, string> Identifiers = new Dictionary<string,string>()
{
    { "somename", "somedescription"},
    { "anothername", "anotherdescription" }
};

XElement xElem = new XElement("Identifiers",
                               Identifiers.Select(x=>new XElement(x.Key,x.Value)));

string xml = xElem.ToString(); //xElem.Save(.....);

<强>输出:

<Identifiers>
  <somename>somedescription</somename>
  <anothername>anotherdescription</anothername>
</Identifiers>

答案 1 :(得分:7)

这很难回答,因为你并没有真正澄清什么是最好的&#39;意味着你。

最快的可能是原始的写作字符串:

var sb = new StringBuilder();
sb.Append("<identifiers>");
foreach(var pair in identifiers)
{
    sb.AppendFormat("<{0}>{1}</{0}>", pair.Key, pair.Value);
}
sb.Append("</identifiers>");

显然,没有处理任何转义为XML,但那可能不是问题,它完全取决于你的字典内容。

最少的代码行怎么样?如果这是您的要求,那么L.B.'s Linq to XML answer's可能是最好的。

最小内存占用怎么样?在那里我要看看删除Dictionary并创建自己的可序列化类,它会丢弃哈希开销和集合功能,而只是存储名称和值。这也可能是最快的。

如果代码简单是您的要求,那么如何使用dynamic或匿名类型而不是Dictionary

var anonType = new
{ 
    somename = "somedescription",
    anothername = "anotherdescription" 
}

// Strongly typed at compile time
anonType.anothername = "new value";

这样你就不会处理魔法字符串&#39;对于集合中属性的名称 - 它将在您的代码中强列入(如果这对您很重要)。

但是,匿名类型没有内置的序列化程序 - 您必须自己编写内容,使用many open source {{之一3}}甚至使用alternatives

加载的方法,最好的方法取决于你将如何使用它。

答案 2 :(得分:3)

前段时间我遇到过类似的问题。我最终使用了这个(取自here

using System;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;

[Serializable()]
public class SerializableDictionary<TKey, TVal> : Dictionary<TKey, TVal>, IXmlSerializable, ISerializable
{
        #region Constants
        private const string DictionaryNodeName = "Dictionary";
        private const string ItemNodeName = "Item";
        private const string KeyNodeName = "Key";
        private const string ValueNodeName = "Value";
        #endregion
        #region Constructors
        public SerializableDictionary()
        {
        }

        public SerializableDictionary(IDictionary<TKey, TVal> dictionary)
            : base(dictionary)
        {
        }

        public SerializableDictionary(IEqualityComparer<TKey> comparer)
            : base(comparer)
        {
        }

        public SerializableDictionary(int capacity)
            : base(capacity)
        {
        }

        public SerializableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer)
            : base(dictionary, comparer)
        {
        }

        public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)
            : base(capacity, comparer)
        {
        }

        #endregion
        #region ISerializable Members

        protected SerializableDictionary(SerializationInfo info, StreamingContext context)
        {
            int itemCount = info.GetInt32("ItemCount");
            for (int i = 0; i < itemCount; i++)
            {
                KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format("Item{0}", i), typeof(KeyValuePair<TKey, TVal>));
                this.Add(kvp.Key, kvp.Value);
            }
        }

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("ItemCount", this.Count);
            int itemIdx = 0;
            foreach (KeyValuePair<TKey, TVal> kvp in this)
            {
                info.AddValue(String.Format("Item{0}", itemIdx), kvp, typeof(KeyValuePair<TKey, TVal>));
                itemIdx++;
            }
        }

        #endregion
        #region IXmlSerializable Members

        void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
        {
            //writer.WriteStartElement(DictionaryNodeName);
            foreach (KeyValuePair<TKey, TVal> kvp in this)
            {
                writer.WriteStartElement(ItemNodeName);
                writer.WriteStartElement(KeyNodeName);
                KeySerializer.Serialize(writer, kvp.Key);
                writer.WriteEndElement();
                writer.WriteStartElement(ValueNodeName);
                ValueSerializer.Serialize(writer, kvp.Value);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
            //writer.WriteEndElement();
        }

        void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
        {
            if (reader.IsEmptyElement)
            {
                return;
            }

            // Move past container
            if (!reader.Read())
            {
                throw new XmlException("Error in Deserialization of Dictionary");
            }

            //reader.ReadStartElement(DictionaryNodeName);
            while (reader.NodeType != XmlNodeType.EndElement)
            {
                reader.ReadStartElement(ItemNodeName);
                reader.ReadStartElement(KeyNodeName);
                TKey key = (TKey)KeySerializer.Deserialize(reader);
                reader.ReadEndElement();
                reader.ReadStartElement(ValueNodeName);
                TVal value = (TVal)ValueSerializer.Deserialize(reader);
                reader.ReadEndElement();
                reader.ReadEndElement();
                this.Add(key, value);
                reader.MoveToContent();
            }
            //reader.ReadEndElement();

            reader.ReadEndElement(); // Read End Element to close Read of containing node
        }

        System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        #endregion
        #region Private Properties
        protected XmlSerializer ValueSerializer
        {
            get
            {
                if (valueSerializer == null)
                {
                    valueSerializer = new XmlSerializer(typeof(TVal));
                }
                return valueSerializer;
            }
        }

        private XmlSerializer KeySerializer
        {
            get
            {
                if (keySerializer == null)
                {
                    keySerializer = new XmlSerializer(typeof(TKey));
                }
                return keySerializer;
            }
        }
        #endregion
        #region Private Members
        private XmlSerializer keySerializer = null;
        private XmlSerializer valueSerializer = null;
        #endregion
}

我刚注意到@Chatumbabub在他的评论中提供了相同的链接。这不是为你做的伎俩吗?

答案 3 :(得分:1)

我不认为你可以使用非常“静态”的XmlSerializer做你想做的事。这里有几个助手,你可以开始使用字典(通用或不通用):

    public static string Serialize(IDictionary dictionary)
    {
        using (StringWriter writer = new StringWriter())
        {
            Serialize(writer, dictionary);
            return writer.ToString();
        }
    }

    public static void Serialize(TextWriter writer, IDictionary dictionary)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");

        using (XmlTextWriter xwriter = new XmlTextWriter(writer))
        {
            Serialize(xwriter, dictionary);
        }
    }

    public static void Serialize(XmlWriter writer, IDictionary dictionary)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");

        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        foreach (DictionaryEntry entry in dictionary)
        {
            writer.WriteStartElement(string.Format("{0}", entry.Key));
            writer.WriteValue(entry.Value);
            writer.WriteEndElement();
        }
    }

使用这些助手,可以使用以下代码:

        Dictionary<string, string> Identifiers = new Dictionary<string,string>
        {
            { "somename", "somedescription"},
            { "anothername", "anotherdescription" }
        };
        Console.WriteLine(Serialize(Identifiers));

将输出:

<somename>somedescription</somename><anothername>anotherdescription</anothername>

你可以适应你的意愿。

答案 4 :(得分:1)

这有帮助吗?

public class CustomDictionary<TValue> : Dictionary<string, TValue>, IXmlSerializable
{
    private static readonly XmlSerializer ValueSerializer;

    private readonly string _namespace;

    static CustomDictionary()
    {
        ValueSerializer = new XmlSerializer(typeof(TValue));
        ValueSerializer.UnknownNode += ValueSerializerOnUnknownElement;
    }

    private static void ValueSerializerOnUnknownElement(object sender, XmlNodeEventArgs xmlNodeEventArgs)
    {
        Debugger.Break();
    }

    public CustomDictionary()
        : this("")
    {
    }

    public CustomDictionary(string @namespace)
    {
        _namespace = @namespace;
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        reader.Read();
        var keepGoing = true;

        while(keepGoing)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                this[reader.Name] = (TValue) reader.ReadElementContentAs(typeof (TValue), null);
            }
            else
            {
                keepGoing = reader.Read();
            }
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        foreach(var kvp in this)
        {
            var document = new XDocument();

            using(var stringWriter = document.CreateWriter())
            {
                ValueSerializer.Serialize(stringWriter, kvp.Value);
            }

            var serializedValue = document.Root.Value;
            writer.WriteElementString(kvp.Key, _namespace, serializedValue);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var dict = new CustomDictionary<string>
        {
            {"Hello", "World"},
            {"Hi", "There"}
        };

        var serializer = new XmlSerializer(typeof (CustomDictionary<string>));

        serializer.Serialize(Console.Out, dict);
        Console.ReadLine();
    }
}

答案 5 :(得分:1)

您可以使用DataContractSerializer来序列化&amp;反序列化Dictionary<string, string>

<强> CODE:

Dictionary<string, string> dictionary = new Dictionary<string, string>();

dictionary.Add("k1", "valu1");
dictionary.Add("k2", "valu2");

System.Runtime.Serialization.DataContractSerializer serializer = new    System.Runtime.Serialization.DataContractSerializer(typeof(Dictionary<string, string>));
System.IO.MemoryStream stream = new System.IO.MemoryStream();

serializer.WriteObject(stream, dictionary);

System.IO.StreamReader reader = new System.IO.StreamReader(stream);

stream.Position = 0;
string xml = reader.ReadToEnd();

答案 6 :(得分:1)

另一种方法是在序列化Identifier类型时子类化XmlTextWriter并控制输出。有点hackish但可能会给你另一个途径。不需要向类型添加任何属性元数据。

public class IdentifierXmlWriter : XmlTextWriter
{
    private bool isIdentifier = false;
    private bool isName = false;
    private bool isDescription = false;

    private readonly string identifierElementName;
    private readonly string nameElementName;
    private readonly string descElementName;

    public IdentifierXmlWriter(TextWriter w) : base(w)
    {
        Type identitierType = typeof (Identifier);

        identifierElementName = (identitierType.GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute ?? new XmlElementAttribute("Identifier")).ElementName;
        nameElementName = (identitierType.GetProperty("Name").GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute ?? new XmlElementAttribute("Name")).ElementName;
        descElementName = (identitierType.GetProperty("Description").GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute ?? new XmlElementAttribute("Description")).ElementName;
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        // If Identifier, set flag and ignore.
        if (localName == identifierElementName)
        {
            isIdentifier = true;
        }
        // if inside Identifier and first occurance of Name, set flag and ignore.  This will be called back with the element name in the Name's Value write call
        else if (isIdentifier && !isName && !isDescription && localName == this.nameElementName)
        {
            isName = true;
        }
        // if inside Identifier and first occurance of Description, set flag and ignore
        else if (isIdentifier && !isName && !isDescription && localName == this.descElementName)
        {
            isDescription = true;
        }
        else
        {
            // Write the element
            base.WriteStartElement(prefix, localName, ns);
        }
    }

    public override void WriteString(string text)
    {
        if ( this.isIdentifier && isName )
            WriteStartElement(text);            // Writing the value of the Name property - convert to Element
        else
            base.WriteString(text);
    }

    public override void WriteEndElement()
    {
        // Close element from the Name property - Ignore
        if (this.isIdentifier && this.isName)
        {
            this.isName = false;
            return;
        }

        // Cliose element from the Description - Closes element started with the Name value write
        if (this.isIdentifier && this.isDescription)
        {
            base.WriteEndElement();
            this.isDescription = false;
            return;
        }

        // Close element of the Identifier - Ignore and reset
        if ( this.isIdentifier )
        {
            this.isIdentifier = false;
        }
        else
            base.WriteEndElement();
    }
}

        List<Identifier> identifiers = new List<Identifier>()
                                           {
                                               new Identifier() { Name = "somename", Description = "somedescription"},
                                               new Identifier() { Name = "anothername", Description = "anotherdescription"},
                                               new Identifier() { Name = "Name", Description = "Description"},
                                           };

这会运行上面的代码并生成您需要的格式,尽管没有换行符和缩进。

        StringBuilder sb = new StringBuilder();
        using ( var writer = new IdentifierXmlWriter(new StringWriter(sb)))
        {
            XmlSerializer xmlSerializer = new XmlSerializer(identifiers.GetType(), new XmlRootAttribute("Identifiers"));
            xmlSerializer.Serialize(writer, identifiers);
        }

        Console.WriteLine(sb.ToString());

答案 7 :(得分:0)

您可以使用C# XML序列化:

private static String SerializeObject<T>(T myObj, bool format) {
    try {
        String xmlizedString = null;
        MemoryStream memoryStream = new MemoryStream();
        XmlSerializer xs = null;
        XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
        if (format)
            xmlTextWriter.Formatting = Formatting.Indented;

        xs = new XmlSerializer(typeof(T), "MyXmlData");

        xs.Serialize(xmlTextWriter, myObj);

        memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
        //eventually
        xmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
        return xmlizedString;
    } 
    catch (Exception e) {
        //return e.ToString();
        throw;
    }
}

(信用:博客条目 Serialize and deserialize objects as Xml using generic types in C# 2.0 。)

要序列化Dictionary,我建议将其转换为情侣列表(可能带有LINQ),因为它不可序列化。

同时检查 Controlling XML Serialization Using Attributes 以编辑条目&#39;名。

好的,在您澄清之后,我想到的第一个,荒谬的(或根本不可行的)任务是以某种方式以编程方式更改元素的Type以反映使用标记的标记的名称标准序列化器。我不知道它是否会起作用。

答案 8 :(得分:0)

另一种选择是在自定义序列化程序中使用反射来获取属性或变量的名称及其值,并使用它来构建XML。这样您就不需要知道传入的内容或其属性的名称。请注意,使用反射很慢,因此如果您要序列化大量对象,则可能无法实现这一目标。