2009-07-15 30 views

回答

17

这在user guide中有详细说明。从http://jaxb.java.net/下载的JAXB包含一个如何一次解析一个块的示例。

当文档是大的,这是 通常是因为有重复 部分在里面。也许这是一个购买 的订单,其中包含大量订单项, 或者它可能是一个包含 大量日志条目的XML日志文件。

这种XML适用于 块处理;主要思路是 分别使用StAX API,运行一个循环,并分别解组单个块 。您的程序将作用于单个块上的 ,然后将其丢弃。 通过这种方式,您将只保留 内存中最多的一个块,这允许您使用 来处理大型文档。

见流,解组 例子和部分解组 例如在JAXB RI分布 更多有关如何做到这一点。该 流,解组例如有 优点是,它可以在任意 窝水平处理块,但它需要 你对付推模式--- JAXB解组将“推”新 块给你,你”你需要 在那里处理它们。

相比之下,局部解组 例如工作在拉取模型(其通常 使处理更容易), 但这种方法有一些局限性 在其它数据绑定部分比 重复部分。

+0

对,这是我在研究这个网站时发现的网站之一,但我无法找到它在第4.4.1节中提到的“流 - 解组”和“部分解组”。 – 2009-07-15 21:32:58

+7

奇怪。你在看哪里?我刚刚从jaxb.dev.java.net/2.1.12下载了JAR,解压缩了它,在“samples”下面是“partial-unmarshalling”和“stream-unmarshalling”。 – skaffman 2009-07-15 21:42:31

24

因为代码很重要,所以这里是一个PartialUnmarshaller谁读大块文件成块。它可以使用这种方式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(); 
    } 
} 
2

伊夫AMSELLEM的答案是相当不错的,但只有当所有元素都完全一样类型的作品。否则,你的unmarshall会抛出一个异常,但读者已经消耗了字节,所以你将无法恢复。相反,我们应该遵循Skaffman的建议并查看来自JAXB jar的示例。

解释它是如何工作的:

  1. 创建JAXB解组。
  2. 将侦听器添加到解组器,以截获适当的元素。这是通过“hacking”ArrayList来完成的,以确保元素在解组后不被存储在内存中。
  3. 创建一个SAX解析器。这是流式传输发生的地方。
  4. 使用unmarshaller为SAX解析器生成处理程序。
  5. 流!

我将该解决方案修改为通用*。但是,这需要一些反思。如果这不行,请查看JAXB JARB中的代码示例。

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); 
     } 
    } 
} 

*此代码尚未经过测试并且仅是为了说明性目的。