2017-10-09 21 views
2

これは、xmlサンプルを "XmlModel"クラスにデシリアライズします。xmlを異なる階層のクラスにデシリアライズしますか?

using System.Collections.Generic; 
using System.IO; 
using System.Xml.Serialization; 

namespace XmlTest 
{ 
    public class DeserializeXml 
    { 
     public XmlModel GetXmlModel() 
     { 
      string xml = @"<?xml version=""1.0"" encoding=""utf-16""?> 
      <root> 
       <foo> 
        <bar>1</bar> 
        <bar>2</bar> 
       </foo> 
      </root>"; 

      var dS = new XmlSerializer(typeof(XmlModel)); 

      var m = new XmlModel(); 
      using (var reader = new StringReader(xml)) 
      { 
       return (XmlModel) dS.Deserialize(reader); 
      } 
     } 
    } 

    [XmlRoot("root")] 
    public class XmlModel 
    { 
     [XmlArray("foo")] 
     [XmlArrayItem("bar")] 
     public List<string> Foo { get; set; } 
    } 
} 

これは、モデルを取得します:

var d = new DeserializeXml(); 
result = d.GetXmlModel(); 

私はレガシーコードで働いていると私はXMLATTRIBUTESを変更する以外XMLModelですクラスを変更することはできませんが。ここでの問題は、次のとおりです。だから今、私はデシリアライザは、このXML出力タイプにおけるXMLModelを飲み込む作るのタスクで立ち往生しています

string xml = @"<?xml version=""1.0"" encoding=""utf-16""?> 
<root> 
    <bar>1</bar> 
    <bar>2</bar> 
</root>"; 

:実際のXMLには「foo」というノードを持っていません。これはXSLTの前処理や他のより複雑なメソッドがなければ可能ですか?

+0

XMLと一致するフォーマットにデシリアライズし、[AutoMapper](http://automapper.org/)のようなものを使用して "アップグレード"することができます。スーパーパフォーマンスではありませんが、あなたの要件がその領域にあるかどうかわかりませんが、それは受け入れられるかもしれません。 –

+1

その他の制限はありますか?別の方法でデシリアライズすることはできますか? –

+0

@Bradley Uffnerプロキシクラス+オートマッペを使用することはオプションですが、ある程度の労力とパフォーマンスのペナルティが必要です。(XSLTプリプロセッサよりも)代わりのシリアライザは、単なるXmlAttributeのトリックがうまくいかない場合(私は彼らが望むと思っていた)、私が考えなかったオプションです。 – TvdH

答えて

1

あなたが行うことによって、それらの属性を使用してconstruct an XmlSerializerそして、あなたのXmlModelの代替XMLの属性を指定するXmlAttributeOverridesを使用することができます高めるために

documentationから次の警告に注意し、

var serializer = new XmlSerializer(typeof(XmlModel), overrides). 

をしかし、 XML直列化インフラストラクチャは、指定された型を直列化および逆直列化するアセンブリを動的に生成します。インフラストラクチャは、これらのアセンブリを見つけて再利用します。次のコンストラクタを使用している場合にのみ、この動作が発生します。

XmlSerializer.XmlSerializer(タイプ)

XmlSerializer.XmlSerializer(タイプ、文字列)

を使用すると、他のコンストラクタのいずれか、同じの複数のバージョンを使用している場合アセンブリが生成され、アンロードされることはありません。その結果、メモリリークが発生し、パフォーマンスが低下します。最も簡単な解決策は、前述の2つのコンストラクタの1つを使用することです。そうでなければ、あなたは

... Hashtableにアセンブリをキャッシュする必要があり、次の静的クラスは2つのシリアライザ、XmlModelの「現在」バージョンのための1、およびで「代替」バージョンのための1つを作成し、キャッシュ

public static class XmlModelSerializer<TRoot> 
{ 
    static XmlSerializer alternateSerializerInstance; 
    static XmlSerializer currentSerializerInstance; 

    public static XmlSerializer AlternateSerializerInstance { get { return alternateSerializerInstance; } } 

    public static XmlSerializer CurrentSerializerInstance { get { return currentSerializerInstance; } } 

    static XmlModelSerializer() 
    { 
     XmlAttributes alternateAttributes = new XmlAttributes 
     { 
      XmlElements = { new XmlElementAttribute("bar") }, 
     }; 
     XmlAttributeOverrides alternateOverrides = new XmlAttributeOverrides(); 
     alternateOverrides.Add(typeof(XmlModel), "Foo", alternateAttributes); 
     alternateSerializerInstance = new XmlSerializer(typeof(TRoot), alternateOverrides); 

     XmlAttributes currentAttributes = new XmlAttributes 
     { 
      XmlArray = new XmlArrayAttribute("foo"), 
      XmlArrayItems = { new XmlArrayItemAttribute("bar") }, 
     }; 
     XmlAttributeOverrides currentOverrides = new XmlAttributeOverrides(); 
     currentOverrides.Add(typeof(XmlModel), "Foo", currentAttributes); 
     currentSerializerInstance = new XmlSerializer(typeof(TRoot), currentOverrides); 
    } 
} 

二つの異なるシリアライザ、それぞれの可能なXML形式のいずれかを使用することで、あなたはあなたのレガシーXmlModelタイプにすべての変更を加える避けることができます:<bar>要素は、外側コンテナ要素を欠いています。

次に、フォームの平坦化されたXMLをデシリアライズする

<root> 
    <bar>1</bar> 
    <bar>2</bar> 
</root> 

あなたは、単にだろう:両方のフォーマットで直列化復元を示す

var dS = XmlModelSerializer<XmlModel>.AlternateSerializerInstance; 
using (var reader = new StringReader(xml)) 
{ 
    return (XmlModel) dS.Deserialize(reader); 
} 

サンプルfiddleを。

+0

これは、上の[XmlElement( "bar")]を使用していることを示すだけでなく、公開リスト Foo {get;セット; }は、 "foo"ノードを省略し、2つの非直列化を動的に切り替える方法も示します。 – TvdH

1

逆シリアル化の代替方法が公開されている場合は、これが機能します。 XmlSerializerよりも速くなくても、それと同じくらい速いはずです。生のxmlにXmlReaderを開き、最初の "data"要素に移動し、データをリストにダンプしてから、XmlModelを入力して返します。

LINQPadファイル利用可能here

public XmlModel GetXmlModel() 
{ 
    string xml = @"<?xml version=""1.0"" encoding=""utf-16""?> 
     <root> 
       <bar>1</bar> 
       <bar>2</bar> 
     </root>"; 
    using (var reader = XmlReader.Create(new StringReader(xml))) 
    { 
     reader.MoveToContent(); 
     var data = new List<string>(); 
     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element) 
      { 
       var element = XNode.ReadFrom(reader) as XElement; 
       switch (element.Name.LocalName) 
       { 
        case "bar": 
         { 
          data.Add(element.Value); 
          break; 
         } 
       } 
      } 
     } 
     return new XmlModel() { Foo = data }; 
    } 
} 

これは明らかにあなたのbarクラスがstringのように、シンプルな組み込み型以上であれば、より複雑になります。

+1

あなたの答えはhttp://msdn.microsoft.com/en-us/library/system.xml.linq.xnode.readfrom.aspxから適応されたように見えますが、残念ながらMSDNコードにはバグがあります。スキップしますXMLが字下げされていないときの要素分析については、[この回答](https://stackoverflow.com/a/18282052/3744182)を参照してください。個人的には、 'XElement'にロードし、メモリ内のすべてを行うことをお勧めします。これは、' XmlReader'を直接操作するのは難しいからです。 – dbc

+0

私はその例を参考にして見ました。あなたの提案も私の好む方法かもしれませんが、私はできるだけ早く物事を維持しようとしていました。 –

関連する問題