Распаковка XML в три списка различных объектов с использованием STAX Parser - PullRequest
11 голосов
/ 11 мая 2019

Можно ли использовать синтаксический анализатор STAX для эффективного анализа XML-документа с несколькими списками объектов различных классов (POJO). Точная структура моего 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>

Я использую STAX Parser, т.е. библиотеку XStream, ссылка: XStream

Это работает абсолютно нормально, пока мой XML содержит список объектов одного класса, но я не знаю, как обращаться с XML, который содержит список объектов различных классов.

Любая помощь будет очень признательна, и, пожалуйста, дайте мне знать, если я не предоставил достаточно информации или неправильно сформулировал вопрос.

Ответы [ 3 ]

7 голосов
/ 14 мая 2019

Вы можете использовать Декларативное потоковое сопоставление (DSM) библиотека синтаксического анализа потока для простого преобразования сложного XML в класс Java. Он использует StAX для разбора XML.

Я пропускаю получение тега notes и добавляю поле внутри тегов class_x_object для демонстрации.

Вот 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>

Прежде всего, вы должны определить соответствие между данными XML и полями вашего класса в формате yaml или JSON.

Вот определения соответствия:

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-класс, который вы хотите десериализовать:

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-код для разбора XML:

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

Вот вывод:

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

UPDATE:

Как я понял из вашего комментария, вы хотите читать большой XML-файл в виде потока. и обрабатывать данные во время чтения файла.

DSM позволяет выполнять обработку данных во время чтения XML.

Объявите три разные функции для обработки частичных данных.

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

Регистрация функции в 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);

изменить файл сопоставления для вызова зарегистрированной функции

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 
3 голосов
/ 19 мая 2019

Вы можете использовать Java Architecture для привязки XML JAXB и Unmarshall, используя классы POJO, как упомянуто ниже.

Сначала создайте классы POJO (я взял несколько узлов из вашего файла XML и создалPOJO. Вы можете сделать то же самое для остальных).Ниже приведен 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>

Ниже приведены классы POJO для Root, category_alpha, list_a, list_b, class_a_object и class_b_object

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

Импорт аналогичных импортов 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 {

}

Вот основной класс

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

}

Используя получатели в корневом объекте и другие объекты, которые вы можете получитьсписок всех объектов внутри корня аналогичен приведенному ниже.

List<CategoryAlpha> categoryAlphaList = emps.getCategoryAlphaList();
0 голосов
/ 13 мая 2019

Я создал парсер для предоставленного примера. 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);
    }
}

Может быть, вы можете предоставить фактический XML и ожидаемый результат?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...