Dictionary <string,string =“”>序列化:在ReadXml()之后,跳过后续的XML元素

时间:2015-12-21 21:06:37

标签: c# xml serialization ixmlserializable

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

    public SerializableStringDictionary Parameters { get; set; }

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

例如,我有一个<MaintenanceIndicators>列表。我只在下面显示一个,但这是代码中的List<MaintenanceIndicator>。具有多个条目的列表的序列化工作正常,但反序列化多个仅在列表中给出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>

2 个答案:

答案 0 :(得分:2)

您的问题并不包含问题的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(),我能够重现,在阅读上面的解析时,只反序列化了第一个<MaintenanceIndicator>元素。

您的问题是,在ReadXml()中,您不会使用</Parameters>结束节点。来自docs

  

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

未能在reader.Read();结束时调用ReadXml()来读取元素结尾将导致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.MoveToAttribute(string name)之前无需致电reader.GetAttribute(string name)

答案 1 :(得分:0)

如果你需要使用低类型对象(因为字典可能出现),使用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);
    }

让我知道你的情景中是否可以接受这种可能性