2012-01-26 22 views
0

非常に醜いXMLを伝達する「Webサービス」を使用する必要があります。これは私たちによって開発されたものではなく、開発者に適切なXMLを理解させる機会はありません。XmlSerializer属性でこのXMLを解析するにはどうすればよいですか?

また、このWebサービスでは、HTTP GET URLパラメータ(要求本体ではありません)に同じ種類のXMLを受け入れています。なぜなら、それを開発した人はなぜ悪い習慣であるのか分かりません。

ので、このようなXMLをマッピングするための最速の方法です:このようなクラスに

<foo id="document"> 
    <foo id="customer"> 
     <bar name="firstname" value="Joe"/> 
     <bar name="lastname" value="Smith"/> 
     <foo id="address"> 
      <bar name="city" value="New York"/> 
      <bar name="country" value="USA"/> 
     </foo> 
    </foo> 
    <bar name="somemoredata1" value="123"/> 
    <bar name="somemoredata2" value="abc"/> 
</foo> 

public class Document 
{ 
    public Customer Customer { get; set; } 

    public int SomeMoreData1 { get; set; } 

    public string SomeMoreData2 { get; set; } 
} 

public class Customer 
{ 
    public Address Address { get; set; } 

    public string FirstName { get; set; } 

    public string LastName { get; set; } 
} 


public class Address 
{ 
    public string City { get; set; } 

    public string Country { get; set; } 
} 

などを使用。 XMLシリアライザの属性や、可能な限り定型コードを必要としない方法などがあります。

要素名はfoobarですが、解析する必要があるXMLの構造は全く同じ規則に基づいています。

私は、もちろん、これらのクラスに手動でIXmlSerializableを実装するか、単にFooBarクラスを作り、XmlSerializerを有するものを使用しますが、これらのオプションのどれも、良い解決策のように見えることができませんでした。

ご回答いただきありがとうございます。

+1

XmlSerialiserで行う必要がありますか?私はしばしば、XmlReaderを良いXMLで解析してビルドするだけで、狂ったXMLを気にするのは簡単ではないことがよくあります。 –

+0

XmlSerializerで構文解析するのが最も簡単です。あなたがより良いまたはより簡単な解決策を持っているなら、答えを書いてください! :)私はそれについて不思議です。 – Venemo

答えて

1

XMLシリアライザの属性ではできません。指定した属性からフィールド名を取得する方法はありません。

<xsl:template match="foo"> 
    <xsl:element name="{@id}"> 
    <xsl:apply-templates/> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="bar"> 
    <xsl:element name="{@name}"> 
    <xsl:value-of select="@value"/> 
    </xsl:element> 
</xsl:template> 

更新:逆変換のための

トリックを行います、次のラインに沿って、簡単なXSLT - あなたは、手動または前処理XML(おそらく定型を生成する)デシリアライズする必要があります
<xsl:template match="*[count(child::text())=1]"> 
    <bar value="{text()}" name="{local-name()}"/> 
</xsl:template> 

<xsl:template match="*"> 
    <foo id="{local-name()}"> 
    <xsl:apply-templates/> 
    </foo> 
</xsl:template> 
+0

あなたのXSLTのアイデアは今のところ最もエレガントです。それに関するもう一つのこと:シリアライザによって出力されたXMLをどうやって醜いフォーマットに変換できますか?私はXSLTになじみがないので、私は尋ねています。 – Venemo

+0

逆変換にxsltが追加され、もう一方が固定されました。 –

+0

アントンにありがとう! XSLTはその日を保存します。 :) – Venemo

1

私はXMLシリアライザまたはデシリアライザを自分で使用していませんが、LINQを使用してXMLドキュメントをオブジェクトに解析しています。クラスがかなりシンプルな場合は、そのルートを調べることができます。

+0

LINQ to XMLを推奨する場合は+1、しかし、上に貼り付けられたXMLがXML OPを表すものであれば、それは整形式ではない(たとえばルートノードがない)ため、難しいかもしれません。 – Tim

+0

私がXmlSerializerについて考えているのは、定型コードをたくさん書く必要から私を救うからです。 – Venemo

1

XmlSerializerは、他の問題でこのアプローチが実施されているわけではないので、XmlSerializerのために盛り上がっていると言いますから、ここでは別のアプローチがあります。ドキュメントの要素の名前は重要ではないように見えるので、構文解析では無視しますが、テストすることもできます。解析が(要素名にswitchで一般に)キーオフすることを主なものとなり、より快適なXMLフォーマットと:

private static Document ParseDocument(XmlReader xr) 
{ 
    Document doc = new Document(); 
    while(xr.Read()) 
     if(xr.NodeType == XmlNodeType.Element) 
     if(xr.GetAttribute("id") == "customer") 
      doc.Customer = ParseCustomer(xr.ReadSubtree()); 
     else 
      switch(xr.GetAttribute("name")) 
      { 
      case "somemoredata1": 
       doc.SomeMoreData1 = int.Parse(xr.GetAttribute("value")); 
       break; 
      case "somemoredata2": 
       doc.SomeMoreData2 = xr.GetAttribute("value"); 
       break; 
      } 
     //Put some validation of doc here if necessary. 
     return doc; 
} 
private static Customer ParseCustomer(XmlReader xr) 
{ 
    Customer cu = new Customer(); 
    while(xr.Read()) 
    if(xr.NodeType == XmlNodeType.Element) 
     if(xr.GetAttribute("id") == "address") 
     cu.Address = ParseAddress(xr.ReadSubtree()); 
     else 
     switch(xr.GetAttribute("name")) 
     { 
      case "firstname": 
      cu.FirstName = xr.GetAttribute("value"); 
      break; 
      case "lastname": 
      cu.LastName = xr.GetAttribute("value"); 
      break; 
     } 
    //validate here if necessary. 
    return cu; 
} 
private static Address ParseAddress(XmlReader xr) 
{ 
    Address add = new Address(); 
    while(xr.Read()) 
    if(xr.NodeType == XmlNodeType.Element) 
     switch(xr.GetAttribute("name")) 
     { 
     case "city": 
      add.City = xr.GetAttribute("value"); 
      break; 
     case "country": 
      add.Country = xr.GetAttribute("value"); 
      break; 
     } 
    return add; 
} 

まさにかなり(それはへのすてきなXMLとひどくかなりではないではありません作業は中断されますが、それほど悪くない傾向があります)、動作します。同じタイプがドキュメント内の別の場所で表示される複雑な構造では、サブツリーの使用がうまくいく場合があります。外部からの値を設定する静的メソッドを、XmlReaderを取るコンストラクタに置き換えることができます。コントラクタは、クラス不変条件を保証したり、オブジェクトを不変にすることができます。

このアプローチは、同じ種類のアイテム(または大規模な一連のタイプ)の大規模なシリーズとしてデシリアライズしたい大規模なドキュメントの場合には、単独で使用できます。yieldそれらは作成され、最初の応答の遅れにかなりの差をつけることができます。

+0

ジョン。私はこの場合に '使用する 'ことをあなたの使用をほとんど止めました。 'XmlReader'がメソッドに渡され、呼び出し側がそれを引き続き必要とする可能性があります。 –

+0

@JohnSaundersは何をする? OP内のドキュメントは終了時に最後まで読み込まれるので、 'XmlReader'で残された唯一のものはそれを破棄することです。もし彼らが "巻き戻し"したいと思っていたら、何らかの方法(文字列、XmlDocumentなど)で保存しておかなければならず、XmlReaderは 'ReadSubtree() 'Dispose() 'を呼び出すことで、"親 "リーダーのさらなる読み取りを妨げることはありません。確かに、私はおそらくスタックを上回っている「使用している」があるだろうが、どこかで完全に使用されるはずだから、それを良い方法で表現するために入れておく。 –

+1

メソッドに渡されたリソースを削除することは、ベストプラクティスではありません。 –

関連する問題