2009-07-15 9 views
21

XMLバインディングが非常に好まれるように、いくつかのXSDファイルで既にスキーマが提供されている可能性のある大きなXMLファイルを解析する必要があります。 JAXBを使ってファイルを解析することができるかどうか、もしあれば、どのようにすればいいのかを知りたい。JAXBは大きなXMLファイルをチャンクで解析できますか

答えて

17

これは、user guideで詳細に説明されています。 http://jaxb.java.net/からのJAXBダウンロードには、一度に1つのチャンクを解析する方法の例が含まれています。

文書が大きい場合には、その中に繰り返し 部分がありますので、それは通常 です。大量の広告申込情報のリスト をご購入の場合は、 か、おそらく ログエントリが多いXMLログファイルです。

この種類のXMLは、チャンク処理のためには、 に適しています。主なアイデアは、 StAX APIを使用してループを実行し、 個々のチャンクを非マーシャルにすることです 。あなたのプログラムは の単一のチャンクで動作し、それからそれを投げ捨てます。 この方法では、 のメモリに1つのチャンクしか保存されないため、 は大きなドキュメントを処理できます。

はこれを行う方法の詳細についてJAXB RI分布 でストリーミングアンマーシャル 例と部分-アンマーシャル 例を参照してください。 「 ストリーミングアンマーシャリングの例では、それが 任意のネストレベルでチャンクを扱うことができる という利点があり、まだあなたはプッシュモデルに対処する それは--- JAXBのアンマーシャラーは、あなたとあなたに新しい チャンクを「プッシュ」しますが必要です する必要があります。対照的に

、部分アンマーシャリング 例は、(通常容易処理を行った )プルモデルで動作し、 このアプローチは 繰り返し部分以外のデータバインディング部のいくつかの制限 を有しています。

+0

これは私がこれを調べて見つけたサイトの1つですが、セクション4.4.1で言及した「ストリーミングアンマーシャリング」と「部分アンマーシャリング」の例は見つかりませんでした。 –

+7

奇数。あなたはどこを探していますか? jaxb.dev.java.net/2.1.12からJARをダウンロードして解凍しました。「samples」の下に「partial-unmarshalling」と「stream-unmarshalling」があります。 – skaffman

24

コードが重要なので、大きなファイルをチャンクに読み込むのはPartialUnmarshallerです。これは、イヴ・Amsellemの答えはかなり良いですが、すべての要素がまったく同じ型である場合にのみ動作する方法new PartialUnmarshaller<YourClass>(stream, YourClass.class)

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.*; 
import java.io.InputStream; 
import java.util.List; 
import java.util.NoSuchElementException; 
import java.util.stream.Collectors; 
import java.util.stream.IntStream; 

import static javax.xml.stream.XMLStreamConstants.*; 

public class PartialUnmarshaller<T> { 
    XMLStreamReader reader; 
    Class<T> clazz; 
    Unmarshaller unmarshaller; 

    public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException { 
     this.clazz = clazz; 
     this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller(); 
     this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream); 

     /* ignore headers */ 
     skipElements(START_DOCUMENT, DTD); 
     /* ignore root element */ 
     reader.nextTag(); 
     /* if there's no tag, ignore root element's end */ 
     skipElements(END_ELEMENT); 
    } 

    public T next() throws XMLStreamException, JAXBException { 
     if (!hasNext()) 
      throw new NoSuchElementException(); 

     T value = unmarshaller.unmarshal(reader, clazz).getValue(); 

     skipElements(CHARACTERS, END_ELEMENT); 
     return value; 
    } 

    public boolean hasNext() throws XMLStreamException { 
     return reader.hasNext(); 
    } 

    public void close() throws XMLStreamException { 
     reader.close(); 
    } 

    void skipElements(int... elements) throws XMLStreamException { 
     int eventType = reader.getEventType(); 

     List<Integer> types = asList(elements); 
     while (types.contains(eventType)) 
      eventType = reader.next(); 
    } 
} 
+0

これを動作させるには、XMLStreamConstants.START_DOCUMENTなどを使用する必要があります。 –

+0

@MatthiasWuttke静的インポートとして追加できます。 import static javax.xml.stream.XMLStreamConstants。*; –

+1

また、GuavaのInts.asListまたはjava8のIntStream.of(要素).boxed()。collect(Collectors.toList());が必要な場合があります。 –

2

使用することができます。そうしないと、unmarshallは例外をスローしますが、読者はすでにバイトを消費しているため、回復できません。代わりに、Skaffmanのアドバイスに従って、JAXB jarのサンプルを見てください。

それがどのように動作するかを説明するには、次の

  1. は、JAXBのアンマーシャラーを作成します。
  2. 適切な要素を傍受するために、アンマーシャラーにリスナーを追加します。これは、整列されていない要素がメモリに格納されていないことを確実にするために、ArrayListを「ハッキングする」ことによって行われます。
  3. SAXパーサーを作成します。これがストリーミングが起こる場所です。
  4. アンマーシャラーを使用して、SAXパーサーのハンドラーを生成します。
  5. ストリーム!

解決策を汎用*に変更しました。しかし、それはいくつかの反映が必要でした。これがOKでない場合は、JAXB jarのコードサンプルを見てください。

ArrayListAddInterceptor.java

import java.lang.reflect.Field; 
import java.util.ArrayList; 

public class ArrayListAddInterceptor<T> extends ArrayList<T> { 
    private static final long serialVersionUID = 1L; 

    private AddInterceptor<T> interceptor; 

    public ArrayListAddInterceptor(AddInterceptor<T> interceptor) { 
     this.interceptor = interceptor; 
    } 

    @Override 
    public boolean add(T t) { 
     interceptor.intercept(t); 
     return false; 
    } 

    public static interface AddInterceptor<T> { 
     public void intercept(T t); 
    } 

    public static void apply(AddInterceptor<?> interceptor, Object o, String property) { 
     try { 
      Field field = o.getClass().getDeclaredField(property); 
      field.setAccessible(true); 
      field.set(o, new ArrayListAddInterceptor(interceptor)); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

} 

Main.java

public class Main { 
    public void parsePurchaseOrders(AddInterceptor<PurchaseOrder> interceptor, List<File> files) { 
     try { 
      // create JAXBContext for the primer.xsd 
      JAXBContext context = JAXBContext.newInstance("primer"); 

      Unmarshaller unmarshaller = context.createUnmarshaller(); 

      // install the callback on all PurchaseOrders instances 
      unmarshaller.setListener(new Unmarshaller.Listener() { 
       public void beforeUnmarshal(Object target, Object parent) { 
        if (target instanceof PurchaseOrders) { 
         ArrayListAddInterceptor.apply(interceptor, target, "purchaseOrder"); 
        } 
       } 
      }); 

      // create a new XML parser 
      SAXParserFactory factory = SAXParserFactory.newInstance(); 
      factory.setNamespaceAware(true); 
      XMLReader reader = factory.newSAXParser().getXMLReader(); 
      reader.setContentHandler(unmarshaller.getUnmarshallerHandler()); 

      for (File file : files) { 
       reader.parse(new InputSource(new FileInputStream(file))); 
      } 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

*このコードはテストされ、説明のみを目的としておりされていません。

関連する問題