web-dev-qa-db-ja.com

System.Xml.XmlExceptionを防ぐ方法:指定されたエンコーディングの無効な文字

C#で記述されたWindowsデスクトップアプリがあり、ディスクに保存され、サードパーティのプログラムによって作成された多数のXMLファイルをループします。ほとんどすべてのファイルは、次のステートメントに続くLINQコードによって正常にロードおよび処理されます。

XDocument xmlDoc = XDocument.Load(inFileName);
List<DocMetaData> docList =
      (from d in xmlDoc.Descendants("DOCUMENT")
       select new DocMetaData
       {
      File = d.Element("FILE").SafeGetAttributeValue("filename")
         ,
      Folder = d.Element("FOLDER").SafeGetAttributeValue("name")
         ,
      ItemID = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Item ID(idmId)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
         ,
      Comment = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Comment(idmComment)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
         ,
      Title = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Title(idmName)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
         ,
      DocClass = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Document Class(idmDocType)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
       }
      ).ToList<DocMetaData>();

...ここで、inFileNameは次のようなフルパスとファイル名です。

     Y:\S2Out\B0000004\Pet Tab\convert.B0000004.Pet Tab.xml

ただし、いくつかのファイルは次のような問題を引き起こします。

System.Xml.XmlException: Invalid character in the given encoding. Line 52327, position 126.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
at System.Xml.XmlTextReaderImpl.InvalidCharRecovery(Int32& bytesCount, Int32& charsCount)
at System.Xml.XmlTextReaderImpl.GetChars(Int32 maxCharsCount)
at System.Xml.XmlTextReaderImpl.ReadData()
at System.Xml.XmlTextReaderImpl.ParseAttributeValueSlow(Int32 curPos, Char quoteChar, NodeData attr)
at System.Xml.XmlTextReaderImpl.ParseAttributes()
at System.Xml.XmlTextReaderImpl.ParseElement()
at System.Xml.XmlTextReaderImpl.ParseElementContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r)
at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r, LoadOptions o)
at System.Xml.Linq.XDocument.Load(XmlReader reader, LoadOptions options)
at System.Xml.Linq.XDocument.Load(String uri, LoadOptions options)
at System.Xml.Linq.XDocument.Load(String uri)
at CBMI.WinFormsUI.GridForm.processFile(StreamWriter oWriter, String inFileName, Int32 XMLfileNumber) in C:\ProjectsVS2010\CBMI.LatitudePostConverter\CBMI.LatitudePostConverter\CBMI.WinFormsUI\GridForm.cs:line 147
at CBMI.WinFormsUI.GridForm.btnProcess_Click(Object sender, EventArgs e) in C:\ProjectsVS2010\CBMI.LatitudePostConverter\CBMI.LatitudePostConverter\CBMI.WinFormsUI\GridForm.cs:line 105

XMLファイルは次のようになります(このサンプルでは2つのDOCUMENT要素のみを示していますが、多数あります)。

<?xml version="1.0" ?>
<DOCUMENTCOLLECTION>
   <DOCUMENT>
       <FILE filename="e:\S2Out\B0000005\General\D003712420.0001.pdf" outputpath="e:\S2Out\B0000005\General"/>
       <ANNOTATION filename=""/>
       <INDEX name="Comment(idmComment)" value=""/>
       <INDEX name="Document Class(idmDocType)" value="General"/>
       <INDEX name="Item ID(idmId)" value="003712420"/>
       <INDEX name="Original File Name(idmDocOriginalFile)" value="Matrix Aligning 603.24 Criteria to Petition Pages.pdf"/>
       <INDEX name="Title(idmName)" value="Matrix for 603.24"/>
       <FOLDER name="/Accreditation/PASBVE/2004-06"/>
   </DOCUMENT>
   <DOCUMENT>
       <FILE filename="e:\S2Out\B0000005\General\D003712442.0001.pdf" outputpath="e:\S2Out\B0000005\General"/>
       <ANNOTATION filename=""/>
       <INDEX name="Comment(idmComment)" value=""/>
       <INDEX name="Document Class(idmDocType)" value="General"/>
       <INDEX name="Item ID(idmId)" value="003712442"/>
       <INDEX name="Original File Name(idmDocOriginalFile)" value="Contacts at NDU.pdf"/>
       <INDEX name="Title(idmName)" value="Contacts at NDU"/>
       <FOLDER name="/Accreditation/NDU/2006-12/Self-Study"/>
   </DOCUMENT>

LINQステートメントには独自の複雑さがありますが、うまくいくと思います。失敗するのはLOADです。 XDocument Loadのさまざまなコンストラクターを調べ、この例外がスローされる他のいくつかの質問を調査しましたが、これを防ぐ方法について混乱しています。

最後に、ロードに失敗したファイルの52327行目、126行目では、52327行目のこのデータが問題を引き起こしてはならないように見えます(そして最後の文字は103行目です!

<FILE filename="e:\S2Out\B0000004\Pet Tab\D003710954.0001.pdf" outputpath="e:\S2Out\B0000004\Pet Tab"/>
15
John Adams

エンコーディングを制御するために(それが何であるかを知っている場合)、Loadを受け入れるStreamメソッドオーバーライドを使用してファイルをロードできます。

次に、コンストラクターで適切なStreamReaderを指定して、ファイルに対して新しいEncodingを作成できます。

たとえば、西ヨーロッパエンコーディングを使用してファイルを開くには、質問の次のコード行を置き換えます。

XDocument xmlDoc = XDocument.Load(inFileName);

このコードで:

XDocument xmlDoc = null;

using (StreamReader oReader = new StreamReader(inFileName, Encoding.GetEncoding("ISO-8859-1"))) {
    xmlDoc = XDocument.Load(oReader);
}

サポートされているエンコードのリストは MSDNドキュメント にあります。

38
competent_tech

これがあなたのケースであるかどうかはわかりませんが、これは特定のエンコーディングの無効なバイトシーケンスに関連している可能性があります。例: http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences

ロード中にファイルから無効なシーケンスをフィルタリングしてみてください。

2
Igor S.

XmlDocumentは、エンコードされていない文字に遭遇するとすぐに全体を読み込むため、プロセス全体を中止します。できることを処理し、duffビットをスキップ/ログする場合は、XmlTextReaderをご覧ください。 Filestreamから読み込まれたXmlTextReaderは、一度にノードを読み込むため、使用するメモリも大幅に少なくなります。賢くなり、分割して処理を並列化することもできます。

私がこれを持っていたとき、それはそこにアクセント文字のようなものでした:墓、鋭角、ウムラウトなど。

自動化されたプロセスはないため、通常はVisual Studioにファイルを読み込み、波線がなくなるまで悪者を編集します。しかし、理論は健全です。

1
Tony Hopkinson

参照ファイルには、ファイル名には有効ですが、XML属性には無効な文字が含まれています。いくつかのオプションがあります。

  1. ファイル名を変更して、サードパーティのスクリプトを再実行できます。
  2. ベンダーと協力して、問題のある文字を安全にエンコードするパッチを提供できます。
  3. XMLドキュメントを事前に検証し、問題のあるエントリを削除してから処理することができます。
1
phatfingers