13

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

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>

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

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

4

9 回答 9

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>
于 2012-09-20T18:27:44.190 回答
7

这很难回答,因为您并没有真正阐明“最佳”对您意味着什么。

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

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,但这可能不是问题,它完全取决于字典的内容。

最少的代码行呢?如果这是您的要求,那么LB 的 Linq to XML 答案可能是最好的。

最小的内存占用呢?在那里,我将考虑删除Dictionary并创建您自己的可序列化类,该类会降低哈希开销和收集功能,而只支持存储名称和值。这也可能是最快的。

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

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

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

这样,您就不会为集合中的属性名称处理“魔术字符串”——它将在您的代码中被强输入(如果这对您很重要)。

然而匿名类型没有内置的序列化器——你必须自己写一些东西,使用许多开源替代 之一,甚至使用. XmlMediaTypeFormatter

有很多方法可以做到这一点,哪种方法最好取决于您将如何使用它。

于 2012-09-25T09:57:25.783 回答
3

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

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 在他的评论中提供了相同的链接。这不是对你有用吗?

于 2012-09-19T12:04:46.513 回答
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>

你可以适应你的意愿。

于 2012-09-19T15:51:55.763 回答
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();
    }
}
于 2012-09-22T07:30:34.113 回答
1

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

代码:

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();
于 2012-09-22T11:03:13.410 回答
1

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

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());
于 2012-09-24T03:12:25.297 回答
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;
    }
}

(来源:博客条目使用 C# 2.0 中的泛型类型将对象序列化和反序列化为 Xml。)

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

还要检查使用属性控制 XML 序列化 以编辑条目的名称。

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

于 2012-09-14T10:01:05.783 回答
0

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

于 2012-09-20T18:23:10.650 回答