Разделение большого XML-файла с помощью Apache Camel с помощью split, stax, jaxb

#java #spring-boot #jaxb #apache-camel #stax

Вопрос:

У меня есть большой XML-файл (может быть около миллиона записей) на сервере sftp. Я не хочу загружать весь файл в память. Намерение состоит в том, что мой маршрут забирает файл, разбивает его и использует stax builder для итерации по элементам, сопоставляет его с объектом JAXB и отправляет в очередь (или пакет spring) для последующего сохранения в базе данных.

input.xml (например, только 2 записи)

 <data>
    <PRODUCTNUMBER>
        <PRODUCTNUMBER>8D0201075E</PRODUCTNUMBER>
        <CURRGROSSPRICE>427.90</CURRGROSSPRICE>
        <NEXTGROSSPRICE>0.00</NEXTGROSSPRICE>
        <NEXTPRICEDATE>1900-01-01 00:00:00</NEXTPRICEDATE>
        <PRODUCTNAME_FR>Some description</PRODUCTNAME_FR>
        <PRODUCTNAME_NL>Some description</PRODUCTNAME_NL>
    </PRODUCTNUMBER>

    <PRODUCTNUMBER>
        <PRODUCTNUMBER>99630211802</PRODUCTNUMBER>
        <CURRGROSSPRICE>3.78</CURRGROSSPRICE>
        <NEXTGROSSPRICE>0.00</NEXTGROSSPRICE>
        <NEXTPRICEDATE>1900-01-01 00:00:00</NEXTPRICEDATE>
        <PRODUCTNAME_FR>Some description</PRODUCTNAME_FR>
    </PRODUCTNUMBER>
</data>
 

Верблюжий маршрут

 from("sftp:localhost:22/in")
   .split(stax(PartRecords.class)).streaming()
   .marshal().json(JsonLibrary.Jackson, true)
   .to("rabbitmq://rabbitmq:5672/myExchange?queue=partQueueamp;routingKey=queue.part")
   .end();
 

PartRecord.java

 @XmlRootElement(name = "PRODUCTNUMBER")
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@ToString
public class PartRecord implements Serializable {

    @XmlElement(name = "PRODUCTNUMBER")
    private String productNumber;

    @XmlElement(name = "CURRGROSSPRICE")
    private BigDecimal currentPrice;

    @XmlElement(name = "PRODUCTNAME_NL")
    private String partDescriptionNL;

    @XmlElement(name = "PRODUCTNAME_FR")
    private String partDescriptionFR;

}
 

PartRecords.java

 @XmlRootElement(name = "data")
@XmlAccessorType(XmlAccessType.FIELD)
@ToString
public class PartRecords implements Serializable {

    @XmlElement(name = "PRODUCTNUMBER")
    private List<PartRecord> partRecords;

    public List<PartRecord> getPartRecords() {
        if (partRecords == null) {
            partRecords = new ArrayList<>();
        }
        return partRecords;
    }

}
 

Маршрут работает нормально, и сообщение помещается в очередь, но вместо того, чтобы иметь по 1 сообщению на запись, в очередь помещается весь файл в формате json. Я думаю, это нормальное поведение, поэтому мне нужно что-то дополнительное. Я не знаю, хорошо ли иметь 1 сообщение на запись, но я полагаю, что наличие 1 сообщения, содержащего весь файл, также неэффективно.

Вывод текущего поведения

 {
  "partRecords" : [ {
    "productNumber" : "8D0201075E",
    "currentPrice" : 427.90,
    "partDescriptionNL" : "Some description",
    "partDescriptionFR" : "Some description"
  }, {
    "productNumber" : "99630211802",
    "currentPrice" : 3.78,
    "partDescriptionNL" : null,
    "partDescriptionFR" : "Some description"
  }]
}
 

Что я делаю не так? Я использую Spring Boot v2.5.4, Apache Camel v3.11.1. Заранее спасибо.

Ответ №1:

Используйте PartRecord вместо PartRecords в вашем маршрутизаторе:

  from("sftp:localhost:22/in")
   .split(stax(PartRecord.class)).streaming()
   .marshal().json(JsonLibrary.Jackson, true)
   .to("rabbitmq://rabbitmq:5672/myExchange?queue=partQueueamp;routingKey=queue.part")
   .end();