JAXB ~ Динамический анализ нескольких пространств имен - PullRequest
1 голос
/ 20 февраля 2020

Я пытаюсь создать unmarshaller, который будет работать для следующих XML файлов:

<?xml version="1.0" encoding="UTF-8"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd 
  xml:lang="en">
  [...]
</REQ-IF>
<?xml version="1.0" encoding="UTF-8"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd" 
  xmlns:configuration="http://eclipse.org/rmf/pror/toolextensions/1.0"
  xmlns:id="http://pror.org/presentation/id"
  xmlns:xhtml="http://www.w3.org/1999/xhtml">
  [...]
</REQ-IF>
<?xml version="1.0" encoding="UTF-8"?>
  <REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:doors="http://www.ibm.com/rdm/doors/REQIF/xmlns/1.0"
  xmlns:reqif="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:reqif-common="http://www.prostep.org/reqif"
  xmlns:reqif-xhtml="http://www.w3.org/1999/xhtml"
  xmlns:rm="http://www.ibm.com/rm"
  xmlns:rm-reqif="http://www.ibm.com/rm/reqif"
  xmlns:xhtml="http://www.w3.org/1999/xhtml"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  [...]
</REQ-IF>

Все эти файлы имеют одинаковую структуру и основаны на одной и той же вершине. одноуровневое пространство имен, но также содержит различные переменные пространства имен подуровня и другие «вещи» (которые, по моему пониманию, должны быть атрибутами, но не являются), которые необходимо сохранить в системе.

Таким образом далеко, мне удалось добраться до точки, где это много сохраняется:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd">
  [...]
</REQ-IF>

однако мой ожидаемый результат будет выглядеть так:

<?xml version="1.0" encoding="UTF-8"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd 
  xml:lang="en">
  [...]
</REQ-IF>

Таким образом, пространство имен верхнего уровня сохраняется, но пространства имен подуровня и другие «вещи» теряются в процессе импорта / экспорта. Это плохо.

Как я могу сохранить эти другие подпространства имен и другие "вещи", учитывая, что они генерируются динамически?

По сути, я хочу сказать: "сохранить все эти дополнительные атрибуты любым удобным вам способом при разборе XML, и как только вы снова экспортируете XML, перепишите их точно так же, как они были ".

1 Ответ

0 голосов
/ 20 февраля 2020

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

Я бы попытался реализовать что-то подобное (только грубая реализация, детали ниже):

public class MyXmlHandler {

    XMLInputFactory xif = XMLInputFactory.newInstance();

    XMLOutputFactory xof = XMLOutputFactory.newInstance();

    XMLEventFactory xef = XMLEventFactory.newInstance();

    /**
     * Retrieve XMLEvent for root element
     */
    public StartElement getStartElement(String source) throws XMLStreamException {
        XMLEvent event;
        XMLEventReader reader = xif.createXMLEventReader(new StringReader(source));
        while (reader.hasNext()) {
            event = reader.nextEvent();
            if (event.isStartElement()) {
                return event.asStartElement();
            }
            // alternativery you can retrieve here also QNames for first level child elements 
            // and return all this data in some synthetic wrapper class
        }
        return null; // alternatively throw an exception
    }

    /**
     * Write root element, than some content from JAXB elements, than end element
     */
    public void write(
        Marshaller marshaller, 
        Writer writer, 
        StartElement root, 
        List<JAXBElement> elements
    ) throws JAXBException, XMLStreamException {
        XMLEventWriter xew = xof.createXMLEventWriter(writer);
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        xew.add(root);
        for(JAXBElement element : elements) {
            marshaller.marshal(element, xew);
        }
        xew.add(xef.createEndElement(root.getName(), root.getNamespaces()));
        xew.close();
    }

}

И используйте его следующим образом:

// create JAXB context and unmarshaller
JAXBContext ctx = JAXBContext.newInstance(RootClass.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();

// unmarshall XML
JAXBElement<RootClass> element = unmarshaller.unmarshal(source, RootClass.class);
RootClass rootValue = element.getValue();

// extract root element data from XML
StartElement root = handler.getStartElement(data);

// perform some business logic

// create marshaller
Marshaller marshaller = ctx.createMarshaller();

// create list of JAXBElements for root children
List<JAXBElement> elements = new ArrayList<>();
QName qname = ... // construct qualified name or retrieve it from saved structure
// very schematic, names of the child elements depend on your implementation
elements.add(new JAXBElement(qname , rootValue.getChild().getClass(), rootValue.getChild()));
handler.write(marshaller, writer, root, elements);

При сохранении данных элемента root вы также можете сохранить QNames для его потомков в некотором классе-оболочке. До сих пор я вижу, что структура REQ-IF содержит заголовок, основной контент и расширения инструмента. Вы можете сохранить QNames для всех них, а затем использовать их для конструирования элементов JAXB во время процесса маршаллинга.

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