简体   繁体   English

使用 JAXB 解组大 xml 文件

[英]Unmarshal big xml files using JAXB

I have 1-2MB xml files and I would like to load them into the list of objects.我有 1-2MB xml 个文件,我想将它们加载到对象列表中。 I wanted to do it through JAXB because I have the appropriate XSD, only the problem appears when loading these files because it throws me errors saying that there is an unclosed tag somewhere, or something like that, when I check the file, there are no errors there.我想通过 JAXB 来完成,因为我有合适的 XSD,只有在加载这些文件时才会出现问题,因为它会抛出错误,说某处有未关闭的标签,或者类似的东西,当我检查文件时,没有那里的错误。 When I loaded files with a size of eg 40KB, there was no problem, everything was loading properly.当我加载大小为 40KB 的文件时,没有问题,一切都在正确加载。 So I understand there's a problem in jaxb when unmarshaling larger files.所以我知道在解组较大文件时 jaxb 中存在问题。 Is there any way to eat this?这个有什么吃法吗? Another way of unmarshalling is unlikely to be included in the game because each xml file has a slightly different structure and objects that I have created with XSD.另一种解组方式不太可能包含在游戏中,因为每个 xml 文件的结构和对象都与我用 XSD 创建的略有不同。

The structure of xml files: xml个文件的结构:

<Request>
  <Header>
    <Name> </Name>
      <Id> </Id>
  </Header>
  <RequestItems>
   <Request>
     <Header>
       <Name> </Name>
       <Id> </Id>
     </Header>
     <ObjectName>
       <City> </City>
       <Street> </Street>
     </ObjectName>
     </Request>
    </RequestItems>
 </Request>

Inside the RequestItems tags there is a list of Request objects and in the Request ObjectName objects it is different depending on the file.在 RequestItems 标签内有一个 Request 对象列表,在 Request ObjectName 对象中它因文件而异。

I do unmrashal in the simplest way and for smaller files it works fine, and the larger ones have strange syntax errors but I understand that it just cuts lines because it is too long and therefore there are syntactic errors.我以最简单的方式执行 unmrashal,对于较小的文件,它工作正常,而较大的文件有奇怪的语法错误,但我知道它只是削减行,因为它太长,因此存在语法错误。

JAXBContext jaxbContext = JAXBContext.newInstance (Request.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller ();
Request request = (Request) jaxbUnmarshaller.unmarshal (xmlFile);

If I have understood your question correctly then this should work.如果我正确理解了您的问题,那么这应该可行。 I have used Moxy which is an extension of Jaxb and Project Lombok for Getter setter.我使用了 Moxy,它是 Jaxb 的扩展和 Project Lombok 的 Getter setter。

Imagine the following is the large XML with different structures request.xml :想象以下是具有不同结构request.xml的大型 XML :

<request>
    <person>
        <name>Batman</name>
        <age>29</age>
        <job>IT</job>
    </person>
    <animal>
        <name>Tommy</name>
        <age>5</age>
        <type>Dog</type>
    </animal>
    <person>
        <name>Superman</name>
        <age>30</age>
        <job>HR</job>
    </person>
</request>

Following are the related classes and interface:以下是相关的类和接口:

import jakarta.xml.bind.annotation.XmlSeeAlso;

@XmlSeeAlso({Person.class, Animal.class})
public interface XmlSupportExtension {
    Object xmlSupport();
}

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlTransient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@XmlTransient
public class Common implements Serializable {
    private String name;
    private String age;
}
import lombok.*;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class Person extends Common implements XmlSupportExtension {

    private String job;

    @Override
    public Person xmlSupport() {
        return this;
    }
}
import lombok.*;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class Animal extends Common implements XmlSupportExtension {
    private String type;

    @Override
    public Animal xmlSupport() {
        return this;
    }
}
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;


public class MainStack {
    public static void main(String[] args) throws JAXBException, XMLStreamException, FileNotFoundException {
        final String[] EVENT_TYPES = new String[]{"person", "animal"};
        InputStream inputStream = MainStack.class.getResourceAsStream("/request.xml");

        //Create an instance of XMLStreamReader to read the events one-by-one
        final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
        inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
        final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(inputStream);

        //Create an instance of JAXBContext and Unmarshaller for unmarshalling the classes to respective event
        final Unmarshaller unmarshaller = JAXBContext.newInstance(Person.class, Animal.class).createUnmarshaller();

        //Navigate to next and start of the XML Elements
        xmlStreamReader.next();

        //Read Until the end of the file and unmarshall event-by-event
        while (xmlStreamReader.hasNext()) {

            //Check if the initial element is one of the elements from "EVENT_TYPES"
            if (xmlStreamReader.isStartElement() && Arrays.asList(EVENT_TYPES).contains(xmlStreamReader.getLocalName())) {

                //Get the event type
                final String eventType = xmlStreamReader.getLocalName();

                Object event = null;

                System.out.println(eventType);

                // Based on eventType make unmarshaller call to respective event class
                switch (eventType) {
                    case "person":
                        //Unmarshal the Person
                        event = unmarshaller.unmarshal(xmlStreamReader, Person.class).getValue();
                        break;
                    case "animal":
                        //Unmarshal the Animal
                        event = unmarshaller.unmarshal(xmlStreamReader, Animal.class).getValue();
                        break;
                    default:
                        //If NONE of the event type matches then do not convert and make a note
                        System.out.println("XML event does not match any of the required event : " + event);
                        break;
                }

                System.out.println(" After Unmarhsalling : " + event.toString());
            }

            //Move to the next event/element in InputStream
            xmlStreamReader.next();
        }
    }
}

This will produce the following output:这将产生以下 output:

person
 After Unmarhsalling : Person(super=Common(name=Batman, age=29), job=IT)
animal
 After Unmarhsalling : Animal(super=Common(name=Tommy, age=5), type=Dog)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM