1. ホーム
  2. スクリプト・コラム
  3. その他

[解決済み】GAEで完全に有効なXMLをパースすると「Prologではコンテンツが許可されません」と表示される

2022-01-10 04:38:14

質問

XMLをパースしようとしているのですが、例えば次のようなものがあります。

<?xml version="1.0" encoding="utf-8"?> 
<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
    <ListDomainsResult>
        <DomainName>Audio</DomainName>
        <DomainName>Course</DomainName>
        <DomainName>DocumentContents</DomainName>
        <DomainName>LectureSet</DomainName>
        <DomainName>MetaData</DomainName>
        <DomainName>Professors</DomainName>
        <DomainName>Tag</DomainName>
    </ListDomainsResult>
    <ResponseMetadata>
        <RequestId>42330b4a-e134-6aec-e62a-5869ac2b4575</RequestId>
        <BoxUsage>0.0000071759</BoxUsage>
    </ResponseMetadata>
</ListDomainsResponse>

このXMLをパーサーに渡すと

XMLEventReader eventReader = xmlInputFactory.createXMLEventReader(response.getContent());

を呼び出し eventReader.nextEvent(); を何度も繰り返して、欲しいデータを取得します。

ここが奇妙なところなのですが、ローカルサーバーの中ではうまくいっているのです。レスポンスが来て、私はそれをパースして、みんなハッピーになる。問題は、Google App Engineにコードをデプロイしたとき、送信リクエストはまだ動作し、レスポンスXMLは100%同一で正しいように見えますが、レスポンスは以下の例外でパースに失敗することです。

com.amazonaws.http.HttpClient handleResponse: Unable to unmarshall response (ParseError at [row,col]:[1,1]
Message: Content is not allowed in prolog.): <?xml version="1.0" encoding="utf-8"?> 
<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"><ListDomainsResult><DomainName>Audio</DomainName><DomainName>Course</DomainName><DomainName>DocumentContents</DomainName><DomainName>LectureSet</DomainName><DomainName>MetaData</DomainName><DomainName>Professors</DomainName><DomainName>Tag</DomainName></ListDomainsResult><ResponseMetadata><RequestId>42330b4a-e134-6aec-e62a-5869ac2b4575</RequestId><BoxUsage>0.0000071759</BoxUsage></ResponseMetadata></ListDomainsResponse>
javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,1]
Message: Content is not allowed in prolog.
    at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(Unknown Source)
    at com.sun.xml.internal.stream.XMLEventReaderImpl.nextEvent(Unknown Source)
    at com.amazonaws.transform.StaxUnmarshallerContext.nextEvent(StaxUnmarshallerContext.java:153)
    ... (rest of lines omitted)

私はこのXMLに「見えない文字」やUTF8でエンコードされていない文字などがないか、2重、3重、4重にチェックしました。バイト順マークやその類のものがないか、配列でバイトごとに見てみました。何もありません。私が投げかけたあらゆる検証テストに合格しています。さらに不思議なことに、Saxonベースのパーサーを使っても同じことが起こります。しかし、GAE上だけで、私のローカル環境ではいつもうまくいきます。

完全に動作する環境でしかデバッガを実行できない場合、コードをトレースして問題を見つけるのは非常に困難です(GAEでリモートデバッグする良い方法は見つかっていません)。それでも、私が持っている原始的な手段を使って、以下のようなアプローチを100万回ほど試してみました。

  • プロローグ付きXMLとプロローグなしXML
  • 改行がある場合とない場合
  • プロローグに "encoding=" 属性を使用する場合と使用しない場合
  • 両方の改行スタイル
  • HTTP ストリームに存在するチャンキング情報の使用と未使用

そして、これらのほとんどを、相互作用があると思われる複数の組み合わせで試してみたのですが、何も起こりませんでした。私は途方に暮れています。どなたか、このような問題を経験されたことのある方で、何かヒントを与えてくれる方はいらっしゃいますか?

解決方法は?

XMLとXSD(またはDTD)のエンコーディングが異なっている。
XMLファイルのヘッダーです。 <?xml version='1.0' encoding='utf-8'?>
XSDファイルのヘッダー。 <?xml version='1.0' encoding='utf-16'?>

もうひとつ考えられるのは、XML文書型宣言の前に何かがある場合です。つまり、バッファに次のようなものがあるとします。

helloworld<?xml version="1.0" encoding="utf-8"?>  

またはスペースや特殊文字でも構いません。

バッファには、バイトオーダーマーカーと呼ばれる特殊な文字が含まれる可能性があります。バッファをパーサーに渡す前に、次のことを行ってください。

String xml = "<?xml ...";
xml = xml.trim().replaceFirst("^([\\W]+)<","<");