Подтвердить что ты не робот

Как сериализовать коллекцию объектов/словарь в значение <key></key>

Есть ли способ сериализации пары ключ/значение (желательно строго типизированный, но, возможно, также из Словаря) в нужный формат ниже?

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>

Итак, мы ищем примеры кода, как наилучшим образом подойти к этому, чтобы получить желаемый результат.

Изменить: Может, мне лучше объяснить. Мы уже знаем, как сериализовать объекты. То, что мы ищем, - это ответ на определенный тип сериализации... Я разберу вопрос выше

4b9b3361

Ответ 1

Трудно ответить, поскольку вы действительно не разъясняете, что для вас "лучше".

Самый быстрый, вероятно, будет сырой выписывать как строки:

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. Ответ Linq на XML, вероятно, лучший.

Как насчет наименьшего объема памяти? Там я бы посмотрел на отбрасывание Dictionary и создание собственного сериализуемого класса, который снижает хеш-накладные и коллекционные функции в пользу просто сохранения имени и значения. Это может быть и быстрее.

Если простое требование кода - это ваше требование, то как насчет использования dynamic или анонимных типов вместо Dictionary?

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

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

Таким образом, вы не имеете дело с "магическими строками" для имен свойств в своей коллекции - он будет строго введен в ваш код (если это важно для вас).

Однако анонимные типы не имеют встроенного сериализатора - вам нужно что-то написать для себя, использовать один из many открыть источник альтернативы или даже использовать XmlMediaTypeFormatter.

Существует множество способов сделать это, что лучше всего зависит от того, как вы собираетесь его использовать.

Ответ 2

Легко с LINQ в 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>

Ответ 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 предоставил ту же ссылку в своем комментарии. Разве это не трюк для вас?

Ответ 4

Я не думаю, что вы можете делать то, что хотите, с очень "статическим" 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>

Вы можете адаптироваться к своей воле.

Ответ 5

Помогает ли это вообще?

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

Ответ 6

Вы можете использовать DataContractSerializer для сериализации и десериализации 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();

Ответ 7

Другой подход заключается в подклассе XmlTextWriter и управлении выходом при сериализации типа идентификатора. Немного хакерский, но может дать вам еще один путь. Не требует добавления метаданных атрибутов к типам.

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

Ответ 8

Вы можете использовать С# 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;
    }
}

(Кредит: запись в блоге Сериализовать и десериализовать объекты как Xml с использованием общих типов в С# 2.0)

Чтобы сериализовать Dictionary, я бы предложил преобразовать его в список пар (возможно, LINQ), потому что это не сериализуемым.

Также проверьте Управление сериализацией XML с помощью атрибутов для редактирования имен записей.

ОК, после вашего разъяснения, первая, абсурдно трудная (или не выполнимая вообще) задача, которая приходит мне на ум, - это каким-то образом изменить программно Type элементов, чтобы отразить имя тега, используя стандартный сериализатор, Я не знаю, будет ли это работать.

Ответ 9

Альтернативным вариантом будет использование отражения в вашем пользовательском сериализаторе, чтобы получить имя свойства или переменной, равно как и значение, и использовать его для построения XML. Таким образом, вам не нужно будет знать, что вы проходите, или имя его свойств. Имейте в виду, что использование отражения медленное, поэтому, если вы сериализуете большое количество объектов, это может быть не так.