Разбор значений элемента листового узла Xml с использованием JAXB

У меня есть xsd say request.xsd и соответствующие классы, сгенерированные jaxb. Теперь у меня есть файл xml request.xml, который я могу распаковать и создать объект «запрос».

У меня есть много тегов элементов в xml, некоторые из которых доступны несколько раз. Мне нужно создать java.util.List, который должен иметь все значения конечных узлов.

Например :

Ниже мой request.xml:

<Request>
  <Operation>manual</Operation>
  <Work>
     <WorkModule>
          <Name>AXN</Name>
     </WorkModule>
  </Work>
  <Identifier>
     <WorkStatus>
          <WorkName>CCH</WorkName>
     </WorkStatus>
     <WorkStatus>
          <WorkName>TMH</WorkName>
     </WorkStatus>
  </Identifier>
</Request>

Ниже приведен мой сгенерированный JAXB класс запроса. Точно так же есть другие классы, соответствующие каждому элементу xml:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "Operation",
    "Work",
    "Identifier"
})
@XmlRootElement(name = "Request", namespace = "http://www.sprts.com/clm/nso/mahsgd")
public class Request{

    @XmlElement(name = "Operation", required = true)
    protected Operation operation;
    @XmlElement(name = "Work", required = true)
    protected Work work;
    @XmlElement(name = "Identifier", required = true)
    protected Identifier identifier;

    \\ getters and setters
}

Таким образом, используя JAXB, я могу получить неупорядоченный объект запроса, имеющий все значения в файле xml.

Теперь, как мне получить все значения листового узла (операция, имя, workName) в общем виде без использования геттеров из объекта запроса, и каждый из которых затем я могу поместить в некоторую коллекцию, скажем, List. Я слышал, что DOM используется для подобных вещей, но мне нужно использовать JAXB для того же.

(Без использования геттеров из объекта запроса, например String opertaion = request.getOperation(); или String name = request.getWork().getWorkModule().getName();)

--РЕДАКТИРОВАТЬ--

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

--EDIT-- С помощью Doughan & Alexandros и некоторых других удалось добиться того же. Не уверен, что обходной путь (преобразование объекта JAXB в объект DOM в InputSource) является лучшим решением. Ниже приведен рабочий код.

     JAXBContext jc = JAXBContext.newInstance(JAXBObject.class);

     // Create the Document
     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
     DocumentBuilder db = dbf.newDocumentBuilder();
     Document document = db.newDocument();

     // Marshal the Object to a Document
     Marshaller marshaller = jc.createMarshaller();
     marshaller.marshal(jaxbObject, document);

    XPathFactory xpf = XPathFactory.newInstance();
    XPath xp = xpf.newXPath();

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    Source xmlSource = new DOMSource(document);
    Result outputTarget = new StreamResult(outputStream);
    TransformerFactory.newInstance().newTransformer().transform(xmlSource,outputTarget);
    InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
    InputSource source = new InputSource(is);

    NodeList leafNodeObjects = (NodeList) xp.evaluate("//*[not(*)]", source, XPathConstants.NODESET);

    for(int x=0; x<leafNodeObjects.getLength(); x++) {
                    System.out.print("nodeElement = ");
                    System.out.print(leafNodeObjects.item(x).getNodeName());
                    System.out.print(" and node value = ");
                    System.out.println(leafNodeObjects.item(x).getTextContent());
                    inputDtos.add(new InputDto(leafNodeObjects.item(x).getNodeName(),
                            leafNodeObjects.item(x).getTextContent()));
   }

person Suvasis    schedule 20.12.2013    source источник
comment
У вас под рукой есть замечательные объекты, где каждый атрибут/метод имеет какое-то значение. Чего вы пытаетесь достичь, что вам нужен общий способ и что-то вроде DOM для замены вашей типизированной структуры данных? Однако очевидным, но плохим ответом будет использование отражения. Но я призываю вас объяснить, чего вы хотите достичь, чтобы мы могли предложить лучшее решение.   -  person Matthias    schedule 20.12.2013
comment
Используя простые геттеры из объекта запроса, я мог создать список, который отлично работает. У меня есть около 150 полей в xml, поэтому ручное сопоставление каждого элемента xml все еще в порядке, но я ожидаю, что это число будет расти в будущем. В этом случае мне снова нужно проверить поля, добавленные или удаленные из xml, и необходимо выполнить соответствующее сопоставление, чтобы поместить в коллекцию, которую я хочу избежать.   -  person Suvasis    schedule 20.12.2013


Ответы (3)


Из вашего комментария:

Я хочу создать список NodeObject, где NodeObject имеет свойство nodeElement и nodeValue. пример. если у меня есть такой элемент, как Anil, тогда у меня будет один NodeObject для этого элемента с nodeElement = name и nodeValue = property.

Вы можете использовать следующий XPath для получения конечных узлов из любого XML-документа (см.: Как выбрать все конечные узлы с помощью выражения XPath?):

//*[not(*)]

Вот он в действии с использованием javax.xml.xpath API:

import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        XPathFactory xpf = XPathFactory.newInstance();
        XPath xp = xpf.newXPath();

        InputSource xml = new InputSource("input.xml");
        NodeList leafNodeObjects = (NodeList) xp.evaluate("//*[not(*)]", xml, XPathConstants.NODESET);

        for(int x=0; x<leafNodeObjects.getLength(); x++) {
            System.out.print("nodeElement = ");
            System.out.print(leafNodeObjects.item(x).getNodeName());
            System.out.print(" and node value = ");
            System.out.println(leafNodeObjects.item(x).getTextContent());
        }
    }

}

Ниже приведен результат запуска этого демонстрационного кода:

nodeElement = Operation and node value = manual
nodeElement = Name and node value = AXN
nodeElement = WorkName and node value = CCH
nodeElement = WorkName and node value = TMH
person bdoughan    schedule 02.01.2014
comment
Ваш код отлично работает для получения значений конечных узлов, но у меня нет доступа к файлу xml. Я получаю неупорядоченный объект pojo для работы. Есть ли способ использовать DOM поверх pojo вместо xml-файла? - person Suvasis; 03.01.2014
comment
@Suvasis - вы можете попробовать выдать XPath для экземпляра JAXBSource или вы можете маршалировать объекты JAXB в DOM и выдать XPath для этого. - person bdoughan; 03.01.2014
comment
@Doughan - я искал, как преобразовать объект JAXB в InputSource, чтобы я мог применить XPATH через метод оценки, чтобы получить конечный узел, как вы сделали выше. Не могли бы вы указать какую-либо ссылку или намек на ссылку. - person Suvasis; 03.01.2014
comment
@Doughan - Или есть способ преобразовать объект DOM в InputSource? - person Suvasis; 03.01.2014
comment
@Suvasis - вы можете оценить XPath по заголовку Document: ="getelementsbytagname поиск по всем уровням узлов xml"> stackoverflow.com/questions/19932570/ - person bdoughan; 03.01.2014
comment
Спасибо Доган и Александрос. - person Suvasis; 03.01.2014

Мой вопрос: можете ли вы ИЗМЕНИТЬ XSD в соответствии со своими потребностями, или XSD контролируется кем-то другим, и вы ДОЛЖНЫ использовать его как есть?

Это важно из-за того, как работает JAXB. По сути, JAXB переводит XSD в классы Java. Он также может делать обратное (переводить классы Java в XSD). Отношения подробно описаны здесь: http://docs.oracle.com/cd/E21764_01/web.1111/e13758/data_types.htm

В вашем случае я предполагаю, что кто-то написал XSD, который вы использовали для создания классов Java, но эти классы имеют много: предпочитаю использовать метод List getListOfSomethings().

Есть два способа исправить это:

(1) Измените XSD так, чтобы «что-то» было частью сложного типа, который представляет собой последовательность (или что-либо, что заставит JAXB генерировать геттер для списка, согласно моему первоначальному ответу).

Это не всегда возможно. Если XSD контролируется каким-то внешним объектом, который говорит: «Вот как выглядят мои данные, вы должны жить с этим, иначе ваше приложение не сможет читать мои данные», то вы не можете этого сделать. В качестве конкретного примера предположим, что ваше приложение хочет прочитать данные EAD из библиотеки Конгресса США. XSD для него находится здесь: http://www.loc.gov/ead/eadschema.html< /а>. Это XSD, что это такое. Вы не можете изменить это. Если вы измените его, ваше приложение будет работать с ВАШИМ определением данных, которое отличается. Вы должны рассмотреть подход (2) ниже, поскольку у вас нет контроля над XSD.

(2) Не используйте JAXB. Вместо этого используйте XML API, который позволяет запрашивать элементы. Таким образом, вы можете собрать все «Что-то» с помощью (например) запроса XPath (см. http://docs.oracle.com/javase/tutorial/jaxp/xslt/xpath.html).

Вы можете создать класс-оболочку, который загружает XML и имеет метод List getSomethings(). Это было бы в следующих строках:

public class RequestWrapper {
    Document doc;
    public RequestWrapper(String xmlUri) {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        this.doc = builder.parse(xmlUri);
    }

    public List<Something> getSomethings() {
        XPathFactory xPathfactory = XPathFactory.newInstance();
        XPath xpath = xPathfactory.newXPath();
        XPathExpression expr = xpath.compile(<DEFINE A SUITABLE EXPRESSION>);
        NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

        List<Something> somethings = new LinkedList<Something>();
        // loop over the nodelist creating instances of Something
        return somethings;
    }
}

Вот хорошее руководство по использованию XPath со Stax, которое может пригодиться: http://www.vogella.com/articles/JavaXML/article.html

(3) Если вы готовы отказаться от стандартных API-интерфейсов Java, вы можете рассмотреть библиотеку, которая дает вам больший контроль над привязками к Java, например Castor: http://castor.codehaus.org/xml-framework.html

В конечном итоге ваша проблема заключается в том, что либо данные представлены неудобным образом, и в этом случае вы должны сделать (2) или (3), либо ВЫ определили неудобный XSD, и в этом случае вы должны сделать (1).

person Alexandros    schedule 31.12.2013
comment
Я сомневаюсь, что вы можете делать то, что хотите, используя JAXB, поскольку JAXB работает, используя отношения 1-на-1 с определенными определениями класса. Если вы действительно ДОЛЖНЫ использовать JAXB, вы также можете написать класс-оболочку, который берет объект JAXB, а затем собирает все конечные элементы, используя циклы for и вручную вызывая исходные геттеры для объекта JAXB. Таким образом, вы напишете спагетти-код один раз, а затем будете использовать этот класс-оболочку везде... - person Alexandros; 31.12.2013
comment
Спасибо Александрос за предоставленные решения. К сожалению, у меня нет контроля над XSD, и клиент не хочет использовать DOM, поскольку нам придется жестко закодировать все необходимые теги xml в XPATH, что будет сложно поддерживать для большого xml, который имеет более 200 тегов или может расти дальше. будущее. Я попробую кастор, как вы упомянули в третьем варианте, чтобы посмотреть, как это работает. - person Suvasis; 31.12.2013
comment
@Suvasis. Вы можете написать общий XPath для получения конечных узлов из любого XML-документа: stackoverflow.com/a/20888804/383861 - person bdoughan; 02.01.2014

Вы определяете структуру XML или кто-то предоставил вам фиксированный XSD, который вы ДОЛЖНЫ использовать?

Если вы фактически определяете структуру XML-запроса, вы можете использовать такие аннотации, как @XmlElement и @XmlElementWrapper, чтобы JAXB работал с коллекциями. См. дополнительную информацию: http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html

person Alexandros    schedule 30.12.2013
comment
да. У меня есть XSD, но как решить мою проблему. Как использовать XSD для чтения элементов xml, имеющих значения? Я не хочу использовать геттеры для преобразованного xml pojo, чтобы получить эти значения. - person Suvasis; 31.12.2013