2011-01-25 19 views
4

XSLTを使用してXML文書を変換しようとしています。入力として私はwww.wordpress.orgのXHTMLソースコードを持ち、XSLTはサイトのタイトルを取得するダミーの例です(実際には何もできません - 何も変わらない)。Javaでの非常に遅いXSLT変換

私が使用するすべてのAPIまたはライブラリは、変換に約2分かかります! wordpress.orgのソースを見てみると、それは183行だけであることがわかります。私はグーグルでそれはおそらくDOMツリーの構築のためです。どんなにシンプルなXSLTであっても、それは常に2分なので、DOM構築に関連していると考えられますが、とにかく2分かかるとは限りません。ここで

は、サンプルコード(何も特別な)です:

TransformerFactory tFactory = TransformerFactory.newInstance(); 
    Transformer transformer = null; 

    try { 
     transformer = tFactory.newTransformer(
      new StreamSource("/home/pd/XSLT/transf.xslt")); 

    } catch (TransformerConfigurationException e) { 
     e.printStackTrace(); 
    } 

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 

    System.out.println("START"); 
    try { 
     transformer.transform(new SAXSource(new InputSource(
      new FileInputStream("/home/pd/XSLT/wordpress.xml"))), 
      new StreamResult(outputStream)); 
    } catch (TransformerException e) {  
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    System.out.println("STOP"); 

    System.out.println(new String(outputStream.toByteArray())); 

それはJavaが2分間 "一時停止" STARTとSTOPの間です。プロセッサーやメモリーの使用状況を見ても、何も増えません。実際にJVMが停止したようです...

50(これは乱数です))行より長いXMLを変換する経験はありますか?私が読んでいるように、XSLTはその作業を行うために常にDOMツリーを構築する必要があります。私にとっては、高速な変換が不可欠です。事前に

おかげで、問題はコールtransfomer.transformではないです ピョートル

+0

「wordpress.xml」のサイズはどれくらいですか? – Davidann

+0

www.wordpress.orgですXHTML - これは183行(既にラップされています)です –

+0

xmlとxsltファイルを投稿できますか?何か他のもの:あなたの出力がByteArrayOutputStreamの場合、通常は入力はRTF-8ですが出力はできないため、問題が発生する可能性があります。 – luiscolorado

答えて

9

サンプルのHTMLファイルは名前空間を使用していますか?その場合、XMLパーサーが名前空間URIから内容(おそらくスキーマ)を取得しようとしている可能性があります。これは、各実行にちょうど2分かかる場合が考えられます。おそらく1つ以上のTCPタイムアウトです。

InputSourceオブジェクト(WordPress XMLが実際に解析される場所)をインスタンス化するのにかかる時間を計測することで、これが遅延の原因となる可能性が高いことを確認できます。投稿したサンプルファイルを確認した後、宣言された名前空間(xmlns="http://www.w3.org/1999/xhtml")が含まれています。

これを回避するには、URLベースの解決を本質的に無効にする独自のEntityResolverを実装することができます。 DOMを使用する必要があるかもしれません - DocumentBuildersetEntityResolverメソッドを参照してください。ここで

は(注意 - これは未テストです)DOMを使用して、解像度を無効にサンプルです:

try { 
    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder db = dbFactory.newDocumentBuilder(); 
    db.setEntityResolver(new EntityResolver() { 

     @Override 
     public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { 
      return null; // Never resolve any IDs 
     } 
    }); 

    System.out.println("BUILDING DOM"); 

    Document doc = db.parse(new FileInputStream("/home/pd/XSLT/wordpress.xml")); 

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 

    TransformerFactory tFactory = TransformerFactory.newInstance(); 
    Transformer transformer = tFactory.newTransformer(
     new StreamSource("/home/pd/XSLT/transf.xslt")); 

    System.out.println("RUNNING TRANSFORM"); 

    transformer.transform(
      new DOMSource(doc.getDocumentElement()), 
      new StreamResult(outputStream)); 

    System.out.println("TRANSFORMED CONTENTS BELOW"); 
    System.out.println(outputStream.toString()); 
} catch (Exception e) { 
    e.printStackTrace(); 
} 

あなたがSAXを使用したい場合は、カスタムリゾルバを使用していますXMLReaderSAXSourceを使用する必要があります。

+0

確かに、XMLパーザの中には、ドキュメントを検証するものがあります。設定でオフにすることができます。しかし、それはXMLパーサに固有のものです。 OPは彼が使用しているJAXPの実装(Xerces、Saxon、Crimsonなど)について言及しなければならず、そのドキュメントで参照することができます。 – BalusC

+0

ありがとう!そうです、それはDTDとこのDTDに含まれる他のすべてのファイルをダウンロードすることです。 EntityResolverの両方のソリューションはうまくいきますが、1つの制限があります。どのDTDが必要かを知り、キャッシュされたインスタンスを準備してEntityResolverで利用できるようにする必要があります。入力XMLがわからない場合(実行時にクライアントから与えられる)もしそうなら、どのDTDが必要かわからない。トランスフォーマーによってダウンロードされたDTDを「ハイジャック」する(エンティティリゾルバがnullを返すと仮定して)、キャッシュし、次回このDTDがこのキャッシュから返される必要があるときは、 –

+0

カスタムリゾルバでキャッシング/プリロードDTDの道を行く前に、解決しようとしている問題に対してDTDが必要であるかどうかを判断する必要があります。 XSLTをXHTMLに適用しようとしているので、私は推測しません(DTDはXMLの解析段階では解決されており、変換段階では解決されていません)。 –

1

チャンス。永遠に取っているあなたのxsltで何かをしている可能性が高いです。私の提案は、あなたのXSLTをプロファイルし、どのテンプレートが最も長く実行されているかを調べるために、OxygenやXML Spyのようなツールを使用することです。これを決定したら、テンプレートを最適化することができます。

+0

絶対に - 問題はXSLTコードにあります。ポスターがXSLTコードではなくJavaコードを表示することを選択したという事実は、これを調査する場所を実際に知らないことを示唆しています。 –

2

答えがEntityResolverにある可能性が高いと投稿したコメント者はおそらく正しいでしょう。しかし、この解決策は単にスキーマをロードするのではなく、ローカルのファイルシステムからスキーマをロードするだけです。

あなたがプロセスに添付日食せずにそれを試して確認して、Androidデバイス上であなたのコードをデバッグする場合だから、この

db.setEntityResolver(new EntityResolver() { 

    @Override 
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { 
     try { 
     FileInputStream fis = new FileInputStream(new File("classpath:xsd/" + systemId)); 
     InputSource is = new InputSource(fis); 
     return is 
    } catch (FileNotFoundException ex) { 
     logger.error("File Not found", ex); 
     return null; 
    } 
    } 
}); 
+0

ありがとう!そうです、それはDTDとこのDTDに含まれる他のすべてのファイルをダウンロードすることです。 EntityResolverの両方のソリューションはうまく動作しますが、1つの制限があります。どのDTDが必要かを知り、キャッシュされたインスタンスを準備してEntityResolverで利用できるようにする必要があります。入力XMLがわからない場合(実行時にクライアントから与えられる)もしそうなら、どのDTDが必要かわからない。トランスフォーマーによってダウンロードされたDTDを「ハイジャック」する(エンティティリゾルバがnullを返すと仮定して)、キャッシュし、次回このDTDがこのキャッシュから返される必要があるときは、 –

+0

これは比較的簡単です。最初に、DTDのsystemIdを持つファイルが存在するかどうかを確認します(systemIDはファイル名です)。ファイルがローカルに存在しない場合は、URLとしてpublicIdを使用し、DTDをフェッチしてローカルに書き出しますファイルシステム。ダウンロードしたファイルを使用する入力ソースを返します。コードが必要な場合はお知らせください。 –

+0

回答ありがとうございます、残念ながら(おそらく)それほど簡単ではありません。例としてhttp://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdを見てみましょう。エンティティリゾルバを使ってアクセス可能な3つのファイルがあります:xhtml-lat1.ent、xhtml-symbol.ent、xhtml-special.ent。含まれているENTファイルのシステムIDと公開IDを印刷すると、次のようになります。 systemId:file:///home/pd/workspace/XMLTransf/xhtml-lat1.ent publicId: - // W3C // ENTITIES Latin 1 for XHTML // (...同様に他の...) パブリックもシステムIDでも検索できません/( "workspace/XMLTrans"は私のEclipseプロジェクトディレクトリです) –

0

ような何かを行うことができます。私のアプリケーションをデバッグしていたとき、xsltの変換には8秒かかりました。同じプロセスがネイティブコードのiosで10分の1を要しました。いったん私はそれに付随する日食なしでコードを実行した、プロセスは、Cベースの対応するに匹敵する量の時間がかかりました。

+0

これは私の最近の経験、他の人に役立つかもしれません。 – user1532390

+0

お手伝いしました。 http://stackoverflow.com/q/23543699/330457 –