StAXを使用したJAXBおよびXSLT

プロジェクトの1つでは、数百メガバイトから数十ギガバイトの大きなXMLファイルを処理する必要がありました。

さらに、さまざまな「深さ」にあるいくつかのタグのみを引き出す必要がありました。 XSLT「額の中」は、メモリ不足のため壊れました。 ストリームパーサーについて考えて覚えておく必要がありました。



いくつかのXML処理モデルがあります。 最も有名なのはDOMとSAXです。

DOMはXMLドキュメント全体をロードし、その内部表現を構築し、ドキュメント全体をナビゲートする機能を提供します。 一方、SAXは入力ドキュメントを読み取り、要素が認識されると、処理のためにハンドラーを呼び出します。



私の場合、DOMはメモリ消費のためにドロップしました。 SAX APIはハンドラー上に構築されているため、コードが読みにくくなります。 StAXは(SAXのような)ストリームパーサーですが、APIはプルの原則に基づいて構築されています。 つまり、認識された要素はオンデマンドでストリームから「削除」されます。



処理されるデータ構造は非常に複雑で多様であり、処理は非常に重要なため、JAXBを使用して内部表現に変換することが決定されました。



プロジェクトデータはNDAによって閉じられているため、この記事では使用されません。



そして、次のものがあります
XML文書
<data> <dtype_one> <p1>p1_data_1</p1> <p2>p1_data_1</p2> <p3>p1_data_1</p3> <p4>p1_data_1</p4> <p5>p1_data_1</p5> </dtype_one> <dtype_two> <p1>p1_data_2</p1> <p2>p1_data_2</p2> <p3>p1_data_2</p3> <p4>p1_data_2</p4> <p5>p1_data_2</p5> </dtype_two> <WS> <dtype_three> <p1>p1_data_3</p1> <p2>p1_data_3</p2> <p3>p1_data_3</p3> <p4>p1_data_3</p4> <p5>p1_data_3</p5> </dtype_three> </WS> </data>
      
      





それから、タグdtype_one、dtype_two、dtype_threeを選択して処理する必要があります。 ドキュメント内でタグが繰り返されています。 取る
文書概要
 <?xml version="1.0" encoding="UTF-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="data" type="dataType"/> <xs:element name="dtype_one" type="dtype_oneType"/> <xs:element name="dtype_two" type="dtype_twoType"/> <xs:element name="dtype_three" type="dtype_threeType"/> <xs:complexType name="dtype_oneType"> <xs:sequence> <xs:element type="xs:string" name="p1"/> <xs:element type="xs:string" name="p2"/> <xs:element type="xs:string" name="p3"/> <xs:element type="xs:string" name="p4"/> <xs:element type="xs:string" name="p5"/> </xs:sequence> </xs:complexType> <xs:complexType name="dataType"> <xs:sequence> <xs:element type="dtype_oneType" name="dtype_one"/> <xs:element type="dtype_twoType" name="dtype_two"/> <xs:element type="WSType" name="WS"/> </xs:sequence> </xs:complexType> <xs:complexType name="WSType"> <xs:sequence> <xs:element type="dtype_threeType" name="dtype_three"/> </xs:sequence> </xs:complexType> <xs:complexType name="dtype_twoType"> <xs:sequence> <xs:element type="xs:string" name="p1"/> <xs:element type="xs:string" name="p2"/> <xs:element type="xs:string" name="p3"/> <xs:element type="xs:string" name="p4"/> <xs:element type="xs:string" name="p5"/> </xs:sequence> </xs:complexType> <xs:complexType name="dtype_threeType"> <xs:sequence> <xs:element type="xs:string" name="p1"/> <xs:element type="xs:string" name="p2"/> <xs:element type="xs:string" name="p3"/> <xs:element type="xs:string" name="p4"/> <xs:element type="xs:string" name="p5"/> </xs:sequence> </xs:complexType> </xs:schema>
      
      







そして、必要なタグの「要素」の要素があることを確認してください:

  <xs:element name="dtype_one" type="dtype_oneType"/> <xs:element name="dtype_two" type="dtype_twoType"/> <xs:element name="dtype_three" type="dtype_threeType"/>
      
      





スキーマがない場合、IDEAはxmlファイルから完全にスキーマを生成できます。


これは、 XJCが@XmlRootElementアノテーションを生成するためのものです。 プロジェクトはmavenになります。maven-jaxb2-pluginを使用してXJCを呼び出します。 スキーマファイルのすべての「要素」に対して@XmlRootElementを生成するには、次の行をbindings.xjbファイルに追加する必要があります。

 <jaxb:bindings> <jaxb:globalBindings > <xjc:simple/> </jaxb:globalBindings> </jaxb:bindings>
      
      





そして、 pom.xmlのプラグイン構成maven-jaxb2-pluginで接続します

 <bindingDirectory>${project.basedir}/xjb</bindingDirectory>
      
      





さて、実際にはコードに対して、 TagEngineクラスはタグハンドラーのリストを保存し、 それ解析します

  public void process(InputStream inputStream) throws FileNotFoundException, XMLStreamException, TransformerException { //  XMLStreamReader,   XMLInputFactory factory = XMLInputFactory.newFactory(); XMLStreamReader streamReader = factory.createXMLStreamReader(inputStream); //   Stack<String> tagStack = new Stack<String>(); //    while (streamReader.hasNext()) { //   int eventType = streamReader.next(); //    if(eventType == XMLStreamConstants.START_ELEMENT) { //    tagStack.push(streamReader.getName().toString()); //     TagProcessor t = processorMap.get(tagStack); if(t != null) { // ,  t.process(streamReader); tagStack.pop(); } } else if(eventType == XMLStreamConstants.END_ELEMENT) { tagStack.pop(); } } }
      
      





JAXBProcessorクラスは、選択された要素を非整列化します。 XSLTProcessorクラスは、XSLT変換を呼び出します。 これは、有用な作業を行うクラスのようです。

 public class DataOne extends JAXBProcessor<DtypeOne> { private static final String TAG_NAME = "data/dtype_one"; //  public DataOne() throws JAXBException, SAXException { super(DtypeOne.class, TAG_NAME); } //       public DataOne(String schemaFileName) throws JAXBException, SAXException { super(DtypeOne.class, TAG_NAME, schemaFileName); } //     XML  @Override public void doWork(DtypeOne element) { // System.out.println(element.getP1()); } }
      
      





XSLT DataThreeXSLTアプリケーションの例。



起動例(277メガバイトのファイルの処理がエミュレートされます):

スキーマ検証なしのJAXBアンマーシャル
ランタイム:8034ms、277000015バイト処理
使用メモリ:80MB
スキーマ検証によるJAXBアンマーシャル
ランタイム:66180ms、277000015バイト処理
使用メモリ:56MB
 XSLT処理
ランタイム:10604ms、277000015バイト処理
使用メモリ:231MB


すべてはメモリで問題ありません。もちろん、検証によって処理が大幅に遅くなります。



PS。 テストにはMockitoを使用しました (以前はjmockを使用しました)。 私はスパイの可能性が好きでした-ライブ(モックではない)オブジェクトを操作するときに呼び出しとそのパラメーターをインターセプトします

PPS GitHubプロジェクトコード。




All Articles