连载字典<字符串,字符串>构件到XML元素和数据 [英] Serialize Dictionary<string,string> member to XML elements and data

查看:110
本文介绍了连载字典<字符串,字符串>构件到XML元素和数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类产品是序列化到XML。我使用的是标准的System.Xml.Serialization.XmlSerializer序列化和XmlWriter的'作家'对象写入序列化结果到StreamWriter对象。串行器对象序列化现在全班一气呵成:

I have a class 'products' that is serializable to XML. I'm using the standard System.Xml.Serialization.XmlSerializer to serialize and a XmlWriter 'writer' object to write the serialized results to a StreamWriter object. The serializer object now serializes the whole class in one go:

XmlSerializer serializer = new XmlSerializer(typeof(products));
serializer.Serialize(writer, products);



类有一个字典<字符串,字符串>成员称为规格。它是动态建立的,所以我不知道事先键。这里是一个什么样的数据字典可以包含一个例子(键:值):

The class has a Dictionary<string,string> member called 'Specifications'. It is dynamically built, so I don't know the keys beforehand. Here's an example of what data the dictionary may contain (key: value):


  • 的颜色:蓝色

  • 长度:110毫米

  • 宽度:55毫米

我想能够序列化该属性到这个:

I would like to be able to serialize that property into this:

...
<specifications>
  <color>blue</color>
  <length>110mm</length>
  <width>55mm</width>
</specifications>
...



我知道这是可怜的XML设计的,但它符合第三方规范。

I know this is poor XML design, but it has to conform to a 3rd party specification.

有可能,我可以用一个标准的属性?如果不是,我怎么会是能够序列化字典这样?

Is there perhaps a standard attribute that I can use? If not, how would I be able to serialize the dictionary like that?

如果您需要更多的代码片段,让我知道。

If you need more code snippets, let me know.

修改
由于一些变化的要求,我放开词典<的;字符串,字符串取代。相反,我创建了一个类规范:

EDIT: Due to some changes in requirement, I let go of the Dictionary<string,string>. Instead, I created a class "Specification":

public class Specification
{
    public string Name;
    public string Value;
    public bool IsOther;

    public Specification() : this(null, null, false) { }

    public Specification(string name, string value) : this(name, value, false) { }

    public Specification(string name, string value, bool isOther)
    {
        Name = name;
        Value = value;
        IsOther = isOther;
    }
}

要避免有一个重复元素规范在产品类规范的名单,我用复数级规格实现IXmlSerializable接口:

To avoid repeating the element "spec" by having a List of "Specification" in the product class, I use a plural class "Specifications" that implements the IXmlSerializable interface:

public class Specifications: IXmlSerializable
{
    public List<Specification> Specs = new List<Specification>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        //I don't need deserialization, but it would be simple enough now.
        throw new System.NotImplementedException();
    }

    public void WriteXml(XmlWriter writer)
    {
        //write all "standarad", named specs
        //this writes the <color>blue</color>-like elements
        Specs.Where(s => !s.IsOther).ToList().ForEach(s => writer.WriteElementString(s.Name, s.Value));

        //write other specs
        //this writes <other_specs>{name|value[;]}*</other_specs>
        string otherSpecs = string.Join(";", Specs.Where(s => s.IsOther).Select(s => string.Concat(s.Name, "|", s.Value)));
        if (otherSpecs.Length > 0) writer.WriteElementString("other_specs", otherSpecs);
    }
}



类规格应用为:

The class "Specifications" is applied as:

public class Product
{
    public Product()
    {
        Specifications = new Specifications();
    }

    [XmlElement("specs")]
    public Specifications Specifications;

    //this "feature" will not include <specs/> when there are none
    [XmlIgnore]
    public bool SpecificationsSpecified { get { return Specifications.Specs.Any(); } }

    //...
}

感谢您提供的IXmlSerializable和的XmlWriter的例子​​。我不知道的XmlWriter的那个接口和使用 - 它被证明是对我来说是宝贵的灵感。

Thank you for providing examples of IXmlSerializable and XmlWriter. I didn't know that interface and usage of XmlWriter - it proved to be a valuable inspiration for me!

*这是我第一次SO问题。什么是最合适的方式来关闭它?因为它不是一个真正的答案,我(约词典)最初的问题我没有提供这是我自己的答案。

*this was my first SO question. What's the most appropriate way to close it? I didn't provide this as my own answer as it is not a real answer to my initial question (about Dictionary).

推荐答案

假设你的字典值都是简单类型可以转换为字符串,你可以创建自己的的IXmlSerializable 词典包装来存储和检索键和值:

Assuming that your dictionary value are all simple types that can be converted to a string, you can create your own IXmlSerializable dictionary wrapper to store and retrieve the keys and values:

public class XmlKeyTextValueListWrapper<TValue> : CollectionWrapper<KeyValuePair<string, TValue>>, IXmlSerializable
{
    public XmlKeyTextValueListWrapper() : base(new List<KeyValuePair<string, TValue>>()) { } // For deserialization.

    public XmlKeyTextValueListWrapper(ICollection<KeyValuePair<string, TValue>> baseCollection) : base(baseCollection) { }

    public XmlKeyTextValueListWrapper(Func<ICollection<KeyValuePair<string, TValue>>> getCollection) : base(getCollection) {}

    #region IXmlSerializable Members

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        var converter = TypeDescriptor.GetConverter(typeof(TValue));
        XmlKeyValueListHelper.ReadXml(reader, this, converter);
    }

    public void WriteXml(XmlWriter writer)
    {
        var converter = TypeDescriptor.GetConverter(typeof(TValue));
        XmlKeyValueListHelper.WriteXml(writer, this, converter);
    }

    #endregion
}

public static class XmlKeyValueListHelper
{
    public static void WriteXml<T>(XmlWriter writer, ICollection<KeyValuePair<string, T>> collection, TypeConverter typeConverter)
    {
        foreach (var pair in collection)
        {
            writer.WriteStartElement(XmlConvert.EncodeName(pair.Key));
            writer.WriteValue(typeConverter.ConvertToInvariantString(pair.Value));
            writer.WriteEndElement();
        }
    }

    public static void ReadXml<T>(XmlReader reader, ICollection<KeyValuePair<string, T>> collection, TypeConverter typeConverter)
    {
        if (reader.IsEmptyElement)
        {
            reader.Read();
            return;
        }

        reader.ReadStartElement(); // Advance to the first sub element of the list element.
        while (reader.NodeType == XmlNodeType.Element)
        {
            var key = XmlConvert.DecodeName(reader.Name);
            string value;
            if (reader.IsEmptyElement)
            {
                value = string.Empty;
                // Move past the end of item element
                reader.Read();
            }
            else
            {
                // Read content and move past the end of item element
                value = reader.ReadElementContentAsString();
            }
            collection.Add(new KeyValuePair<string,T>(key, (T)typeConverter.ConvertFromInvariantString(value)));
        }
        // Move past the end of the list element
        reader.ReadEndElement();
    }

    public static void CopyTo<TValue>(this XmlKeyTextValueListWrapper<TValue> collection, ICollection<KeyValuePair<string, TValue>> dictionary)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");
        if (collection == null)
            dictionary.Clear();
        else
        {
            if (collection.IsWrapperFor(dictionary)) // For efficiency
                return;
            var pairs = collection.ToList();
            dictionary.Clear();
            foreach (var item in pairs)
                dictionary.Add(item);
        }
    }
}

public class CollectionWrapper<T> : ICollection<T>
{
    readonly Func<ICollection<T>> getCollection;

    public CollectionWrapper(ICollection<T> baseCollection)
    {
        if (baseCollection == null)
            throw new ArgumentNullException();
        this.getCollection = () => baseCollection;
    }

    public CollectionWrapper(Func<ICollection<T>> getCollection)
    {
        if (getCollection == null)
            throw new ArgumentNullException();
        this.getCollection = getCollection;
    }

    public bool IsWrapperFor(ICollection<T> other)
    {
        if (other == Collection)
            return true;
        var otherWrapper = other as CollectionWrapper<T>;
        return otherWrapper != null && otherWrapper.IsWrapperFor(Collection);
    }

    ICollection<T> Collection { get { return getCollection(); } }

    #region ICollection<T> Members

    public void Add(T item)
    {
        Collection.Add(item);
    }

    public void Clear()
    {
        Collection.Clear();
    }

    public bool Contains(T item)
    {
        return Collection.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        Collection.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return Collection.Count; }
    }

    public bool IsReadOnly
    {
        get { return Collection.IsReadOnly; }
    }

    public bool Remove(T item)
    {
        return Collection.Remove(item);
    }

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return Collection.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

和然后使用它像这样:

[XmlRoot("products")]
public class Products
{
    public Products()
    {
        Specifications = new Dictionary<string, string>();
    }

    [XmlIgnore]
    [JsonProperty("specifications")] // For testing purposes, I compare Json.NET serialization before and after XML serialization.  You can remove this.
    public Dictionary<string, string> Specifications { get; set; }

    [XmlElement("specifications")]
    [JsonIgnore] // For testing purposes, I compare Json.NET serialization before and after XML serialization.  You can remove this.
    public XmlKeyTextValueListWrapper<string> XmlSpecifications
    {
        get
        {
            return new XmlKeyTextValueListWrapper<string>(() => this.Specifications);
        }
        set
        {
            value.CopyTo(Specifications = (Specifications ?? new Dictionary<string, string>()));
        }
    }
}



事实上,你的字典中的值是简单类型(可直接转换从和文本)能够避免的XmlSerializer 的嵌套的创作,这是比较复杂的。请参见 一个例子。

The fact that your dictionary values are simple types (directly convertible from and to text) makes it possible to avoid nested creations of XmlSerializer, which is more complex. See here for an example.

这篇关于连载字典&LT;字符串,字符串&GT;构件到XML元素和数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆