【问题标题】:Dictionary<string, string> serialization: after ReadXml(), subsequent XML elements are skippedDictionary<string, string> 序列化:ReadXml() 之后,跳过后续 XML 元素
【发布时间】:2015-12-21 21:06:36
【问题描述】:

在我的一个对象中,我有一本通过实现IXmlSerializable 进行序列化的字典:

    public SerializableStringDictionary Parameters { get; set; }

当我使用普通的 .NET 序列化程序序列化这些对象的列表时,它可以很好地序列化,但是反序列化只执行单个对象 - XML 文件中遵循可序列化字典的元素被跳过。

例如,我有一个&lt;MaintenanceIndicators&gt; 的列表。我在下面只显示一个,但这是代码中的List&lt;MaintenanceIndicator&gt;。具有多个条目的列表的序列化工作正常,但反序列化多个仅在列表中给我 1 MaintenanceIndicator。但是,它的参数属性可以反序列化。代码中不会抛出异常。

我使用以下代码进行反序列化:

    public void ReadXml(XmlReader reader)
    {
        bool wasEmpty = reader.IsEmptyElement;
        // jump to <parameters>
        reader.Read();

        if (wasEmpty)
            return;

        // read until we reach the last element
        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            // jump to <item>
            reader.MoveToContent();

            // jump to key attribute and read
            reader.MoveToAttribute("key");
            string key = reader.GetAttribute("key");

            // jump to value attribute and read
            reader.MoveToAttribute("value");
            string value = reader.GetAttribute("value");

            // add item to the dictionary
            this.Add(key, value);

            // jump to next <item>
            reader.ReadStartElement("item");
            reader.MoveToContent(); // workaround to trigger node type
        }
    }

我在 XML 中的结构如下所示:

<MaintenanceIndicators>
<MaintenanceIndicator Id="1" Name="Test" Description="Test" Type="LimitMonitor" ExecutionOrder="1">
  <Parameters>
    <item key="Input" value="a" />
    <item key="Output" value="b" />
    <item key="Direction" value="GreaterThan" />
    <item key="LimitValue" value="1" />
    <item key="Hysteresis" value="1" />
  </Parameters>
</MaintenanceIndicator>
 <!-- Any subsequent MaintenanceIndicator indicator element will not be read -->
</MaintenanceIndicators>

【问题讨论】:

  • 你有没有在 SO 上尝试过其他一些字典反序列化方法? stackoverflow.com/questions/12554186/…
  • 您的 XML 格式是否正确?它没有 MaintenanceIndicators 集合元素的结束标记。
  • 我已经更新了xml,在源中是正确的,我只是粘贴错了。
  • @thinklarge 我从该示例开始,并将其用于更复杂的字典,但是我只是将其重写为使用属性而不是元素。在应用程序的另一部分,我将其序列化为类似于实体框架中的字符串,使用属性比使用元素小得多。

标签: c# xml serialization ixmlserializable


【解决方案1】:

您的问题不包含您的问题的complete 示例。为了确认,您有以下(简化的)类定义,SerializableStringDictionary 如您的问题所示:

public class MaintenanceIndicator
{
    public MaintenanceIndicator()
    {
        this.Parameters = new SerializableStringDictionary();
    }
    public SerializableStringDictionary Parameters { get; set; }
}

[XmlRoot("MaintenanceIndicators")]
public class MaintenanceIndicators
{
    [XmlElement("MaintenanceIndicator")]
    public List<MaintenanceIndicator> MaintenanceIndicatorList { get; set; }
}

还有以下 XML:

<MaintenanceIndicators>
  <MaintenanceIndicator>
    <Parameters>
      <item key="Input" value="a" />
      <item key="Output" value="b" />
    </Parameters>
  </MaintenanceIndicator>
  <MaintenanceIndicator>
    <Parameters>
      <item key="Input2" value="a" />
      <item key="Output2" value="b" />
    </Parameters>
  </MaintenanceIndicator>
</MaintenanceIndicators>

在这种情况下,使用您的 ReadXml(),我能够重现,在阅读上面的解析时,只有第一个 &lt;MaintenanceIndicator&gt; 元素被反序列化。

您的问题是,在ReadXml() 中,您不使用&lt;/Parameters&gt; 端节点。来自docs

当此方法返回时,它必须从头到尾读取整个元素,包括其所有内容。与 WriteXml 方法不同,框架不会自动处理包装元素。您的实施必须这样做。不遵守这些定位规则可能会导致代码生成意外的运行时异常或损坏数据。

未能在ReadXml() 末尾调用reader.Read(); 读取元素末尾将导致XML 中的所有后续元素被跳过或以其他方式读取错误。

因此你应该修改ReadXml()如下:

    public void ReadXml(XmlReader reader)
    {
        bool wasEmpty = reader.IsEmptyElement;
        // jump to <parameters>
        reader.Read();

        if (wasEmpty)
            return;

        // read until we reach the last element
        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            // jump to <item>
            reader.MoveToContent();

            // jump to key attribute and read
            string key = reader.GetAttribute("key");

            // jump to value attribute and read
            string value = reader.GetAttribute("value");

            // add item to the dictionary
            this.Add(key, value);

            // jump to next <item>
            reader.ReadStartElement("item");
            reader.MoveToContent(); // workaround to trigger node type
        }
        // Consume the </Parameters> node as required by the documentation
        // https://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.readxml%28v=vs.110%29.aspx
        // Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. 
        reader.Read();
    }

请注意,在调用reader.GetAttribute(string name) 之前无需调用reader.MoveToAttribute(string name)

【讨论】:

    【解决方案2】:

    如果您需要使用低类型对象(可能会出现字典),使用 javascript 序列化程序而不是 XML 会更加灵活。

    例如:

        private void button1_Click(object sender, EventArgs e)
        {
            List<Dictionary<string, string>> datas = new List<Dictionary<string, string>>();
            Dictionary<string,string> d1 = new Dictionary<string,string> ();
            d1.Add ( "key11","value11");
            d1.Add ( "key12","value12");
            Dictionary<string,string> d2 = new Dictionary<string,string> ();
            d2.Add ( "key21","value21");
            d2.Add ( "key22","value22");
            datas.Add(d1);
            datas.Add(d2);
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string serialized = serializer.Serialize (datas);
    
            List<Dictionary<string, string>> deserialized = serializer.Deserialize<List<Dictionary<string, string>>>(serialized);
        }
    

    让我知道这种序列化在您的场景中是否可以接受

    【讨论】:

    • 其实我周围还有很多XML也是序列化/反序列化的,这只是其中的一小部分。
    • 如果您不必保证向后兼容性,那么使用 JavaScriptSerializer 您可以序列化-反序列化至少您目前的所有序列化-反序列化。此外,您可以本机序列化反序列化字典、动态和其他“低类型”对象(因此您可以避免手动处理与所有问题相关的序列化)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多