Reputation: 2440
I am trying to create an unmarshaller that will work for the following XML files:
<?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>
All those files are structurally the same and are based of the same top-level namespace, but also contain a variety of variable sub-level namespaces and other "things" (which by my understanding should be attributes, but are not), which need to be saved in the system.
Thus far, I have managed to get to the point where this much is saved:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<REQ-IF xmlns="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd">
[...]
</REQ-IF>
however, my intended result would look like this:
<?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>
So the top-level namespace is saved, but the sub-level namespaces and other "things" are lost in the import/export process. This is bad.
How can I save those other sub-namespaces and other "things", considering that they are dynamically generated?
Basically, what I want to say is "save all these extra attributes in any way you like while parsing the XML, and once you export the XML again, re-write them exactly as they were".
Upvotes: 1
Views: 628
Reputation: 63
If your main use case is to read ReqIF files, consider that there is an open source implementation of a ReqIF (de)serializer at https://www.eclipse.org/rmf/
Upvotes: 1
Reputation: 953
Unfortunately it seems that JAXB alone is not capable of manage all the namespace prefixes dynamically and you need to combine it with another parsing mechanism.
I would try to implement something like this (only rough implementation, details below):
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();
}
}
And use it like this:
// 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);
When preserving root element data, you can save also QNames for its children in some wrapper class. So far I see the REQ-IF structure contains header, core content and tool extensions. You could save QNames for all of them and then use them for constructing JAXB elements during marshalling process.
Upvotes: 0