如何(de)使用字符串数组值序列化可序列化字典?

时间:2018-03-19 17:20:02

标签: c# .net dictionary xml-serialization

我需要(在)C#(.NET Framework 4.5.2)中将一个类序列化为XML,该XML具有带string个键和string[]数组值的字典属性。我在另一个问题上使用了this answer中提到的SerializableDictionary<TKey, TValue>实现,这意味着我的属性属于SerializableDictionary<string, string[]>类型。

将其序列化为XML文件看起来效果不错;但是,反序列化总是因System.InvalidOperationException而失败。

即使仅对字典进行反序列化也会发生这种情况。请参阅下面的MSTest单元测试,该测试可以重现问题:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using My.Namespace.Collections; // Where SerializableDictionary is defined.
using System.Xml.Serialization;
using System.IO;
using System.Linq;

namespace My.Namespace.Collections.Tests
{
    /// <summary>
    /// Provides unit test methods for the <see cref="SerializableDictionary{TKey, TValue}"/> class.
    /// </summary>
    [TestClass]
    public class SerializableDictionaryTests
    {
        /// <summary>
        /// A basic test that the <see cref="SerializableDictionary{TKey, TValue}"/> class has working
        /// (de)serialization.
        /// </summary>
        [TestMethod]
        [Timeout(100)]
        public void SerializableDictionary_BasicTest()
        {
            // Arrange.
            var sourceDictionary = new SerializableDictionary<string, string[]>();
            SerializableDictionary<string, string[]> destinationDictionary;
            var serializer = new XmlSerializer(typeof(SerializableDictionary<string, string[]>));

            var list1 = new string[] { "apple", "banana", "pear" };
            var list2 = new string[] { "carrot", "broccoli", "cauliflower", "onion" };
            var list3 = new string[] { "beef", "chicken" };

            sourceDictionary.Add("fruits", list1);
            sourceDictionary.Add("vegetables", list2);
            sourceDictionary.Add("meats", list3);

            // Act.
            using (var stream = new MemoryStream())
            {
                serializer.Serialize(stream, sourceDictionary);
                stream.Position = 0;
                destinationDictionary = (SerializableDictionary<string, string[]>)serializer.Deserialize(stream);
            }

            // Assert.
            // NOTE: We don't get this far because it crashes on the last statement above in the using block.
            Assert.AreEqual(3, destinationDictionary.Keys.Count);
            Assert.IsTrue(destinationDictionary.ContainsKey("fruits"));
            Assert.IsTrue(destinationDictionary.ContainsKey("vegetables"));
            Assert.IsTrue(destinationDictionary.ContainsKey("meats"));

            Assert.AreEqual(3, destinationDictionary["fruits"].Length);
            Assert.IsTrue(destinationDictionary["fruits"].Contains("apple"));
            Assert.IsTrue(destinationDictionary["fruits"].Contains("banana"));
            Assert.IsTrue(destinationDictionary["fruits"].Contains("pear"));

            Assert.AreEqual(4, destinationDictionary["vegetables"].Length);
            Assert.IsTrue(destinationDictionary["vegetables"].Contains("carrot"));
            Assert.IsTrue(destinationDictionary["vegetables"].Contains("broccoli"));
            Assert.IsTrue(destinationDictionary["vegetables"].Contains("cauliflower"));
            Assert.IsTrue(destinationDictionary["vegetables"].Contains("onion"));

            Assert.AreEqual(2, destinationDictionary["meats"].Length);
            Assert.IsTrue(destinationDictionary["meats"].Contains("beef"));
            Assert.IsTrue(destinationDictionary["meats"].Contains("chicken"));
        }
    }
}

例外是在serializer.Deserialize电话,并显示:

Test method My.Namespace.Collections.Tests.SerializableDictionaryTests.SerializableDictionary_BasicTest threw exception: 
System.InvalidOperationException: There is an error in XML document (8, 8). ---> System.InvalidOperationException: There is an error in XML document (8, 8). ---> System.InvalidOperationException: <ArrayOfString xmlns=''> was not expected.

为什么我会收到此异常,如何避免此异常?如果可能的话,我想避免仅为这一个属性使用自定义XML序列化。

编辑#1:

我制作了一个小型控制台程序,它运行上面的构造和序列化代码,然后使用FileStream类将生成的XML写入文件。这是它的内容:

<?xml version="1.0"?>
<dictionary>
  <item>
    <key>
      <string>fruits</string>
    </key>
    <value>
      <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <string>apple</string>
        <string>banana</string>
        <string>pear</string>
      </ArrayOfString>
    </value>
  </item>
  <item>
    <key>
      <string>vegetables</string>
    </key>
    <value>
      <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <string>carrot</string>
        <string>broccoli</string>
        <string>cauliflower</string>
        <string>onion</string>
      </ArrayOfString>
    </value>
  </item>
  <item>
    <key>
      <string>meats</string>
    </key>
    <value>
      <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <string>beef</string>
        <string>chicken</string>
      </ArrayOfString>
    </value>
  </item>
</dictionary>

编辑#2:

经过一些额外测试后,我将测试中的TValue声明的SerializableDictionary<TKey, TValue>类型更改为不同的内容,并检查它是否有效,我可以确认它是否正常TValuestring,但如果将其设置为我的自定义可序列化类,则会产生相同的错误,即使该类使用SerializableAttribute修饰或实现IXmlSerializable接口。所以,问题实际上不仅仅是字符串数组。

2 个答案:

答案 0 :(得分:1)

我很高兴您找到了解决方案,并设法使其正常运行。不确定您是否仍然愿意接受解决方案。尽管如此,这是你可以用Dictionary原样做的事情。

虽然.NET的XmlSerializer不支持字典,但有些库可以为您完成。一个这样的lib是SharpSerializer(SourceNuGet),它将任何类型序列化为二进制或XML。

用法非常简单:

var serializer = new SharpSerializer(); 
serializer.Serialize(dict, "test.xml");

<强>输入

var dict = new Dictionary<string, string[]> 
{
    { "nikhil", new string[] { "nikhil.0@so.com", "nikhil.1@so.com" } } 
};

<强>输出

<Dictionary name="Root" type="System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  <Items>
    <Item>
      <Simple value="nikhil" />
      <SingleArray>
        <Items>
          <Simple value="nikhil.0@so.com" />
          <Simple value="nikhil.1@so.com" />
        </Items>
      </SingleArray>
    </Item>
  </Items>
</Dictionary>

Fiddle

答案 1 :(得分:0)

经过几天的努力,我得出的结论是,我使用的Serializable<TKey, TValue>实现无法用于处理数组和其他对象。

我求助于在我的类上实现IXmlSerializable接口,将字典值序列化为WriteXml方法中的列表。在ReadXml中,我只是反序列化存储的列表,并将其值重新放回字典中,同时检查没有重复的键存储为列表项的附加属性。它很笨重,但它确实有效。