2017-08-23 8 views
2

辞書シリアル化のよりクリーンなXMLを得るために、私はIXmlSerializableを実装するカスタムクラスを作成しました。xmlに字下げ/改行があると、辞書のカスタムシリアル化が失敗する

マイカスタムクラスをこのように定義されています

public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable 
{ 
    private const string XmlElementName = "MyData"; 
    private const string XmlAttributeId = "Id"; 

    public XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     reader.MoveToContent(); 
     while (reader.Read()) 
     { 
      if(reader.LocalName == XmlElementName) 
      { 
       var tag = reader.GetAttribute(XmlAttributeId); 
       var content = reader.ReadElementContentAsString(); 

       this.Add(tag, content); 
      } 
     } 
    } 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     foreach (string key in this.Keys) 
     { 
      writer.WriteStartElement(XmlElementName); 
      writer.WriteAttributeString(XmlAttributeId, key); 
      writer.WriteString(this[key]); 
      writer.WriteEndElement(); 
     } 
    } 
} 

私のコードは、このXMLスニペットで動作します。私は、この縮小さXMLを持っている場合

<MyCollection xmlns="http://schemas.datacontract.org/2004/07/MyProject" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <MyData Id="1">some content</MyData> 
    <MyData Id="2">some other content</MyData> 
</MyCollection> 

はしかし、私のコードは例外をスローします。

<MyCollection xmlns="http://schemas.datacontract.org/2004/07/MyProject" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><MyData Id="1">some content </MyData><MyData Id="2">some other content</MyData></MyCollection> 

例外は次のとおりです。

System.InvalidOperationException: The ReadElementContentAsString method is not supported on node type EndElement 

ReadElementContentAsStringに電話がかけられました。

コードを修正するにはどうすればよいですか?

私が使用して問題をREPROことができます。

var xml = @"<MyCollection xmlns=""http://schemas.datacontract.org/2004/07/MyProject"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><MyData Id=""1"">some content </MyData><MyData Id=""2"">some other content</MyData></MyCollection>"; 

var raw = Encoding.UTF8.GetBytes(xml); 

var serializer = new DataContractSerializer(typeof(MyCollection)); 

using (var ms = new MemoryStream(raw)) 
{ 
    var result = serializer.ReadObject(ms); // Exception throws here 
} 
+0

コードを 'if(reader.LocalName == XmlElementName && reader.NodeType == XmlNodeType.Element)'に変更するとどうなりますか?あなたは終わりのタグにいるときに要素の内容を読み込もうとしているようです。 – Pawel

答えて

2

あなたの問題は、次のノードの先頭にreader.ReadElementContentAsString()位置リーダーではなく、現在のノードの最後ということです。次に、reader.Read()への無条件呼び出しにより、次のノードが消費されます。そのノードが空白の場合、損害は発生しませんが、ノードが要素の場合、要素はスキップされます。

あなたMyCollectionこの問題を修正し、次のバージョン:私はReadXml()は、このように破損MyCollection要素の終わりを過ぎて読まれていないことを確認XmlReader.ReadSubtree()を使用することにより

  • public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable 
    { 
        public XmlSchema GetSchema() 
        { 
         return null; 
        } 
    
        public void ReadXml(XmlReader reader) 
        { 
         using (var subReader = reader.ReadSubtree()) 
         { 
          XmlKeyValueListHelper.ReadKeyValueXml(subReader, this); 
         } 
         // Consume the EndElement also (or move past the current element if reader.IsEmptyElement). 
         reader.Read(); 
        } 
    
        public void WriteXml(System.Xml.XmlWriter writer) 
        { 
         XmlKeyValueListHelper.WriteKeyValueXml(writer, this); 
        } 
    } 
    
    public static class XmlKeyValueListHelper 
    { 
        private const string XmlElementName = "MyData"; 
        private const string XmlAttributeId = "Id"; 
    
        public static void WriteKeyValueXml(System.Xml.XmlWriter writer, ICollection<KeyValuePair<string, string>> collection) 
        { 
         foreach (var pair in collection) 
         { 
          writer.WriteStartElement(XmlElementName); 
          writer.WriteAttributeString(XmlAttributeId, pair.Key); 
          writer.WriteString(pair.Value); 
          writer.WriteEndElement(); 
         } 
        } 
    
        public static void ReadKeyValueXml(System.Xml.XmlReader reader, ICollection<KeyValuePair<string, string>> collection) 
        { 
         if (reader.IsEmptyElement) 
         { 
          reader.Read(); 
          return; 
         } 
    
         reader.ReadStartElement(); // Advance to the first sub element of the list element. 
         while (reader.NodeType != XmlNodeType.EndElement) 
         { 
          if (reader.NodeType == XmlNodeType.Element && reader.LocalName == XmlElementName) 
          { 
           var tag = reader.GetAttribute(XmlAttributeId); 
           string content; 
           if (reader.IsEmptyElement) 
           { 
            content = string.Empty; 
            // Move past the end of item element 
            reader.Read(); 
           } 
           else 
           { 
            // Read content and move past the end of item element 
            content = reader.ReadElementContentAsString(); 
           } 
           collection.Add(new KeyValuePair<string, string>(tag, content)); 
          } 
          else 
          { 
           // For instance a comment. 
           reader.Skip(); 
          } 
         } 
         // Move past the end of the list element 
         reader.ReadEndElement(); 
        } 
    } 
    

    いくつかの注意将来の要素 - IXmlSerializableを実装する際に間違いがあります。

  • reader.NodeType == XmlNodeType.Element && reader.LocalName == XmlElementNameをチェックすると、コメントなどの予期しないタイプのノードは無視されます。

作業.Net fiddle

+0

それは魅力のように働くことに感謝します。 XmlReaderの使用は明白ではありません:( –

関連する問題