如何使用字符串数组值(反)序列化可序列化的字典? [英] How do I (de)serialize a serializable dictionary with string array values?

查看:68
本文介绍了如何使用字符串数组值(反)序列化可序列化的字典?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要将C#(.NET Framework 4.5.2)中的类与XML进行反序列化,该XML具有带有string键和string[]数组值的字典属性.我在另一个问题上使用的是此答案中提到的SerializableDictionary<TKey, TValue>实现,这意味着我的媒体资源类型为SerializableDictionary<string, string[]>.

I need to (de)serialize a class in C# (.NET Framework 4.5.2) to and from XML which has a dictionary property with string keys and string[] array values. I am using the SerializableDictionary<TKey, TValue> implementation mentioned in this answer on another question, so that means my property is of type SerializableDictionary<string, string[]>.

将此序列化为XML文件似乎可以正常进行;但是,反序列化总是会因System.InvalidOperationException而失败.

Serialization of this to an XML file looks like it works alright; however, deserializing always fails with a System.InvalidOperationException.

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

This occurs even when just deserializing the dictionary on its own. See the below MSTest unit test which reproduces the problem:

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调用中显示为:

The exception is at the serializer.Deserialize call, and reads:

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序列化.

Why am I getting this exception and how can I avoid it? I would like to avoid having to resort to custom XML serialization for just this one property, if possible.

编辑#1:

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

I made a small console program which runs the above construction and serialization code, then writes the resulting XML to a file using the FileStream class. Here is its contents:

<?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:

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

After some additional testing changing the TValue type of the SerializableDictionary<TKey, TValue> declaration in my tests to different things and check whether it works at all, I can confirm that it works okay if TValue is string, but the same error results if it is set to, say, a custom serializable class of mine, even if that class is decorated with SerializableAttribute or implements the IXmlSerializable interface. So, the problem is actually bigger than just with string arrays.

推荐答案

在为此苦苦挣扎了几天之后,我得出的结论是,我正在使用的Serializable<TKey, TValue>实现无法通过按摩来处理数组,并且其他对象.

After struggling for a couple days with this, I came to the conclusion that the Serializable<TKey, TValue> implementation I was using can't be massaged to work with arrays and other objects.

我诉诸于在类上实现IXmlSerializable接口,将字典值序列化为WriteXml方法中的列表.在ReadXml中,我只是反序列化存储的列表,然后将其值放回字典中,同时检查列表项上是否没有作为附加属性存储的重复键.它笨拙,但是可以.

I resorted to implementing the IXmlSerializable interface on my class, with the dictionary values serialized as a list in the WriteXml method. In ReadXml, I simply deserialize the stored list, and put its values back into the dictionary while checking that there are no duplicate keys stored as an additional property on the list items. It's clunky, but it works.

这篇关于如何使用字符串数组值(反)序列化可序列化的字典?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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