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

Dictionary<string, string> serialization: after ReadXml(), subsequent XML elements are skipped

在我的一个对象中,我有一个通过实现 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>

如果您需要使用低类型对象(如字典可能出现的那样),请使用 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);
    }

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

您的问题不包含您的问题的 complete 示例。为了确认,您有以下(简化的)class 定义,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:

When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.

ReadXml()末尾调用reader.Read();读取元素end失败将导致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)