如何使用 Jackson 解析 XML 流式传输文件 API

How to use Jackson to parse XML files with streaming API

我正在寻找一种使用 kotlin 解析大型 xml 的方法。

我常用的JSON解析器是Jackson,我知道它也可以用来解析xml。

源文件太大,无法使用 DOM 方法进行解析,我必须改为使用流 API。我可以找到几个关于如何使用 jackson streaming API 和 JSON 的示例,但没有关于 XML 的内容。 文档 https://github.com/FasterXML/jackson-dataformat-xml

Although module implements low-level (JsonFactory / JsonParser / JsonGenerator) abstractions, most usage is through data-binding level. This because a small number of work-arounds have been added at data-binding level, to work around XML peculiarities:

这让我担心如果使用此库的 XML 流媒体方法甚至可能 and/or 支持。

读取树结构需要进程启动(例如 <element> 用于 XML{ / [ 用于 JSON) 这种方式不可能在以流方式处理时读取整个对象。

让根包装器和一大堆汽车(为简洁起见,我使用 lombok 注释):

@Getter
@Setter
@JacksonXmlRootElement
@NoArgsConstructor
@AllArgsConstructor
static class CarBook {
    @JacksonXmlProperty(isAttribute = true)
    private int version;
    @JacksonXmlElementWrapper(localName = "cars")
    @JacksonXmlProperty(localName = "car")
    private List<Car> cars;
}

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
static class Car {
    private String model;
    private String plate;
}

然后,在所有列表(可能是其他成员)被完全读取之前,您无法获得 CarBook 对象。

通常的方法是使用 XMLStreamReader 并逐个检查令牌,但您可以使用 jackson 使用 XmlMapper 方法解析整个对象:

/**
 * Method for reading a single XML value from given XML-specific input
 * source; useful for incremental data-binding, combining traversal using
 * basic Stax {@link XMLStreamReader} with data-binding by Jackson.
 * 
 * @since 2.4
 */
public <T> T readValue(XMLStreamReader r, Class<T> valueType) throws IOException {
    return readValue(r, _typeFactory.constructType(valueType));
} 

以大(1,2G)文件为例:

<CarBook version="1"><cars>
<car><model>Alfa Romeo Spider</model><plate>27437</plate></car>
<car><model>Almera</model><plate>6429</plate></car>
<car><model>Audi 80 and 90</model><plate>4898</plate></car>
<car><model>Audi A3</model><plate>21259</plate></car>
<car><model>Audi A4</model><plate>21056</plate></car>
<car><model>Audi Coupé</model><plate>5623</plate></car>
<car><model>Austin Metro</model><plate>26446</plate></car>
<car><model>BMW 3 Series</model><plate>16338</plate></car>
<car><model>BMW 5 Series</model><plate>29859</plate></car>
...

那个,你可以偷偷看

public static void main(String... args) throws IOException, XMLStreamException {
    XmlMapper xm = new XmlMapper();
    XMLInputFactory xif = XMLInputFactory.newInstance();
    XMLStreamReader xr = xif.createXMLStreamReader(new FileInputStream(/* 1,2G file */ "/home/josejuan/tmp/all.cars.xml"));

    // you must to read step by step
    while (xr.hasNext()) {
        xr.next();
        if (xr.getEventType() == START_ELEMENT) {
            System.out.println(xr.getLocalName());
            if ("car".equals(xr.getLocalName())) {
                Car car = xm.readValue(xr, Car.class);
                System.out.println(car);
                if ("21056".equals(car.getPlate()))
                    break;
            }
        }
    }

    System.out.println("== End Of Process ==");
}

有输出

CarBook
cars
car
WithLazyJackson.Car(model=Alfa Romeo Spider, plate=27437)
car
WithLazyJackson.Car(model=Almera, plate=6429)
car
WithLazyJackson.Car(model=Audi 80 and 90, plate=4898)
car
WithLazyJackson.Car(model=Audi A3, plate=21259)
car
WithLazyJackson.Car(model=Audi A4, plate=21056)
== End Of Process ==

在 19.800.000 辆中只读取了 5 辆汽车