简体   繁体   English

使用STAX Parser将XML解组为三个不同对象的列表

[英]Unmarshalling XML to three lists of different objects using STAX Parser

Is there a way I can use STAX parser to efficiently parse an XML document with multiple lists of objects of different classes (POJO). 有没有办法可以使用STAX解析器有效地解析XML文档与多个不同类的对象列表(POJO)。 The exact structure of my XML is as follows (class names are not real) 我的XML的确切结构如下(类名不是真的)

<?xml version="1.0" encoding="utf-8"?>
<root>
    <notes />
    <category_alpha>
        <list_a>
            <class_a_object></class_a_object>
            <class_a_object></class_a_object>
            <class_a_object></class_a_object>
            <class_a_object></class_a_object>
            .
            .
            .
        </list_a>
        <list_b>
            <class_b_object></class_b_object>
            <class_b_object></class_b_object>
            <class_b_object></class_b_object>
            <class_b_object></class_b_object>
            .
            .
            .
        </list_b>
    </category_alpha>
    <category_beta>
        <class_c_object></class_c_object>
        <class_c_object></class_c_object>
        <class_c_object></class_c_object>
        <class_c_object></class_c_object>
        <class_c_object></class_c_object>
        .
        .
        .
        .
        .
    </category_beta>
</root>

I have been using the STAX Parser ie XStream library, link: XStream 我一直在使用STAX Parser即XStream库,链接: XStream

It works absolutely fine as long as my XML contains list of one class of objects but I dont know how to handle an XML that contains list of objects of different classes. 只要我的XML包含一类对象的列表但它不知道如何处理包含不同类的对象列表的XML,它就可以正常工作。

Any help would be really appreciated and please let me know if I have not provided enough information or I haven't phrased the question properly. 任何帮助将非常感谢,如果我没有提供足够的信息或我没有正确地提出问题,请告诉我。

You can use Declarative Stream Mapping (DSM) stream parsing library to easily convert complex XML to java class. 您可以使用声明性流映射(DSM)流解析库轻松地将复杂XML转换为Java类。 It uses StAX to parse XML. 它使用StAX来解析XML。

I skip getting notes tag and add a field inside class_x_object tags for demostration. 我跳过获取注释标记并在class_x_object标记内添加一个字段以进行演示。

Here is the XML: 这是XML:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <notes />
    <category_alpha>
        <list_a>
            <class_a_object>
                <fieldA>A1</fieldA>
            </class_a_object>
            <class_a_object>
                <fieldA>A2</fieldA>
            </class_a_object>
            <class_a_object>
                <fieldA>A3</fieldA>
            </class_a_object>

        </list_a>
        <list_b>
            <class_b_object>
                <fieldB>B1</fieldB>
            </class_b_object>
            <class_b_object>
                <fieldB>B2</fieldB>
            </class_b_object>
            <class_b_object>
                <fieldB>B3</fieldB>
            </class_b_object>
        </list_b>
    </category_alpha>
    <category_beta>
        <class_c_object>
          <fieldC>C1</fieldC>
        </class_c_object>
        <class_c_object>
          <fieldC>C2</fieldC>
        </class_c_object>
        <class_c_object>
          <fieldC>C3</fieldC>
        </class_c_object>
    </category_beta>
</root>

First of all, you must define the mapping between XML data and your class fields in yaml or JSON format. 首先,您必须以yaml或JSON格式定义XML数据和类字段之间的映射。

Here are the mapping definitions: 以下是映射定义:

result:     
   type: object
   path: /root   
   fields:
     listOfA:
       type: array
       path: .*class_a_object  # path is regex
       fields:
          fieldOfA:
            path: fieldA
     listOfB:
       type: array
       path: .*class_b_object
       fields:
          fieldOfB:
            path: fieldB 
     listOfC:
       type: array
       path: .*class_c_object
       fields:
          fieldOfC:
            path: fieldC 

Java class that you want to deserialize: 要反序列化的Java类:

public class Root {
    public List<A> listOfA;
    public List<B> listOfB;
    public List<C> listOfC;

    public static class A{
        public String fieldOfA;
    }
    public static class B{
        public String fieldOfB;
    }
    public static class C{
        public String fieldOfC;
    }

}   

Java Code to parse XML: 用于解析XML的Java代码:

DSM dsm=new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.XML).create(Root.class);
Root root =  (Root)dsm.toObject(xmlFileContent);
// write root object as json
dsm.getObjectMapper().writerWithDefaultPrettyPrinter().writeValue(System.out, object);

Here is output: 这是输出:

{
  "listOfA" : [ {"fieldOfA" : "A1"}, {"fieldOfA" : "A2"}, {"fieldOfA" : "A3"} ],
  "listOfB" : [ {"fieldOfB" : "B1"}, {"fieldOfB" : "B2"}, "fieldOfB" : "B3"} ],
  "listOfC" : [ {"fieldOfC" : "C1"}, {"fieldOfC" : "C2"}, {"fieldOfC" : "C3"} ]
}

UPDATE: 更新:

As I understand from your comment, you want to read big XML file as a stream. 正如我从您的评论中理解的那样,您希望将大型XML文件作为流读取。 and process data while you are reading the file. 在阅读文件时处理数据。

DSM allows you to do process data while you are reading XML. DSM允许您在阅读XML时执行过程数据。

Declare three different functions to process partial data. 声明三个不同的函数来处理部分数据。

FunctionExecutor processA=new FunctionExecutor(){
            @Override
            public void execute(Params params) {

                Root.A object=params.getCurrentNode().toObject(Root.A.class);

                // process aClass; save to db. call service etc.
            }
        };
FunctionExecutor processB=new FunctionExecutor(){
            @Override
            public void execute(Params params) {

                Root.B object=params.getCurrentNode().toObject(Root.B.class);

                // process aClass; save to db. call service etc.
            }
        };

FunctionExecutor processC=new FunctionExecutor(){
            @Override
            public void execute(Params params) {

                Root.C object=params.getCurrentNode().toObject(Root.C.class);

                // process aClass; save to db. call service etc.
            }
        };

Register function to DSM 向DSM注册功能

 DSMBuilder builder = new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.XML);

       // register function
        builder.registerFunction("processA",processA);
        builder.registerFunction("processB",processB);
        builder.registerFunction("processC",processC);

        DSM dsm= builder.create();
        Object object =  dsm.toObject(xmlContent);

change Mapping file to call registered function 更改映射文件以调用已注册的函数

result:     
   type: object
   path: /root   
   fields:
     listOfA:
       type: object
       function: processA  # when 'class_a_object' tag closed processA function will be executed.
       path: .*class_a_object  # path is regex
       fields:
          fieldOfA:
            path: fieldA
     listOfB:
       type: object
       path: .*class_b_object
       function: processB# register function
       fields:
          fieldOfB:
            path: fieldB 
     listOfC:
       type: object
       path: .*class_c_object
       function: processC# register function
       fields:
          fieldOfC:
            path: fieldC 

You could use Java Architecture for XML binding JAXB and Unmarshall using the POJO classes as mentioned below. 您可以使用Java Architecture for XML绑定JAXB和Unmarshall,使用下面提到的POJO类。

Create POJO classes first (I have taken few nodes from your XML file and created the POJO. You can do the similar for the rest). 首先创建POJO类(我从您的XML文件中获取了少量节点并创建了POJO。其余的可以执行类似的操作)。 Below is the XML I considered. 以下是我考虑过的XML。

<?xml version="1.0" encoding="utf-8"?>
<root>
    <category_alpha>
        <list_a>
            <class_a_object></class_a_object>
            <class_a_object></class_a_object>
            <class_a_object></class_a_object>
            <class_a_object></class_a_object>
        </list_a>
        <list_b>
            <class_b_object></class_b_object>
            <class_b_object></class_b_object>
            <class_b_object></class_b_object>
            <class_b_object></class_b_object>
        </list_b>
    </category_alpha>
</root>

Below are the POJO classes for Root, category_alpha, list_a, list_b, class_a_object and class_b_object 以下是Root,category_alpha,list_a,list_b,class_a_object和class_b_object的POJO类

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name = "root")
@XmlAccessorType (XmlAccessType.FIELD)
public class Root {

    @XmlElement(name = "category_alpha")
    private List<CategoryAlpha> categoryAlphaList = null;

    public List<CategoryAlpha> getCategoryAlphaList() {
        return categoryAlphaList;
    }

    public void setCategoryAlphaList(List<CategoryAlpha> categoryAlphaList) {
        this.categoryAlphaList = categoryAlphaList;
    }
}

Import the similar java imports to the above class here in the following classes. 在以下类中将类似的java导入导入到上面的类中。

@XmlRootElement(name = "category_alpha")
@XmlAccessorType (XmlAccessType.FIELD)
public class CategoryAlpha {

    @XmlElement(name = "list_a")
    private List<ListAClass> list_a_collectionlist = null;

    @XmlElement(name = "list_b")
    private List<ListBClass> list_b_collectionlist = null;


    public List<ListAClass> getList_a_collectionlist() {
        return list_a_collectionlist;
    }


    public void setList_a_collectionlist(List<ListAClass> list_a_collectionlist) {
        this.list_a_collectionlist = list_a_collectionlist;
    }


    public List<ListBClass> getList_b_collectionlist() {
        return list_b_collectionlist;
    }


    public void setList_b_collectionlist(List<ListBClass> list_b_collectionlist) {
        this.list_b_collectionlist = list_b_collectionlist;
    }
}

@XmlRootElement(name = "list_a")
@XmlAccessorType (XmlAccessType.FIELD)
public class ListAClass {

    @XmlElement(name = "class_a_object")
    private List<ClassAObject> classAObjectList = null;

    public List<ClassAObject> getClassAObjectList() {
        return classAObjectList;
    }

    public void setClassAObjectList(List<ClassAObject> classAObjectList) {
        this.classAObjectList = classAObjectList;
    }
}

@XmlRootElement(name = "list_b")
@XmlAccessorType (XmlAccessType.FIELD)
public class ListBClass {

    @XmlElement(name = "class_b_object")
    private List<ClassBObject> classBObjectList = null;

    public List<ClassBObject> getClassBObjectList() {
        return classBObjectList;
    }

    public void setClassBObjectList(List<ClassBObject> classBObjectList) {
        this.classBObjectList = classBObjectList;
    }
}

@XmlRootElement(name = "class_a_object")
@XmlAccessorType (XmlAccessType.FIELD)
public class ClassAObject {

}

@XmlRootElement(name = "class_b_object")
@XmlAccessorType (XmlAccessType.FIELD)
public class ClassBObject {

}

Here is the Main class 这是Main类

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

public class UnmarshallMainClass {

    public static void main(String[] args) throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

        // This root object contains all the list of objects you are looking for
        Root emps = (Root) jaxbUnmarshaller.unmarshal( new File("sample.xml") );
    }

}

By using the getters in the root object and other objects you can retrieve the list of all objects inside the root similar like below. 通过在根对象和其他对象中使用getter,您可以检索根内部所有对象的列表,如下所示。

List<CategoryAlpha> categoryAlphaList = emps.getCategoryAlphaList();

I have created a parser for provided example. 我已经为提供的示例创建了一个解析器。 https://github.com/sbzDev/stackoverflow/tree/master/question56087924 https://github.com/sbzDev/stackoverflow/tree/master/question56087924

import com.thoughtworks.xstream.annotations.XStreamAlias;

import java.util.List;

@XStreamAlias("root")
public class Root {

    String notes;

    @XStreamAlias("category_alpha")
    CategoryAlpha categoryAlpha;


    @XStreamAlias("category_beta")
    List<C> listC;

    static class CategoryAlpha {

        @XStreamAlias("list_a")
        List<A> listA;

        @XStreamAlias("list_b")
        List<B> listB;
    }

    @XStreamAlias("class_a_object")
    static class A {
    }

    @XStreamAlias("class_b_object")
    static class B {
    }

    @XStreamAlias("class_c_object")
    static class C {
    }
}

Parser: 分析器:

import com.thoughtworks.xstream.XStream;

public class SampleRootParser {

    public Root parse(String xmlContent){
        XStream xstream = new XStream();
        xstream.processAnnotations(Root.class);
        return  (Root)xstream.fromXML(xmlContent);
    }
}

Maybe you can provide actual XML and expected result? 也许你可以提供实际的XML和预期的结果?

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

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