简体   繁体   中英

JAXB: unmarshal from XML subtree?

My JAVA program uses an internal class hierarchy which resembles GPX 1.1, but is not identical. since rewriting it to conform 1:1 to GPX is a huge effort, I'd like to change it bit by bit, ie reading the <metadata> subtree into the Class MetadataType as generated with xjc from the XSD file

the remaining GPX file is parsed with DOM, until <metadata> shows up:

private void parseMetadata(MetadataType metadata, Element element) throws JAXBException {
    try {
        System.out.println(element.getNodeName()); // output: metadata
        JAXBContext context = JAXBContext.newInstance(MetadataType.class);

        javax.xml.bind.Unmarshaller u = context.createUnmarshaller();

        JAXBElement<MetadataType> meta = u.unmarshal(element, MetadataType.class);
        metadata = meta.getValue();

        } catch (Exception e) { 
            e.printStackTrace();
        }
    System.out.println(metadata.getName()); // NULL
    System.out.println(metadata.getAuthor().getName()); // NULL
}

throws javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"metadata"). Expected elements are <{http://www.topografix.com/GPX/1/1}gpx> at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647) javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"metadata"). Expected elements are <{http://www.topografix.com/GPX/1/1}gpx> at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)

the class looks like this:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "metadataType", propOrder = {
"name",
"desc",
"author",
"copyright",
"link",
"time",
"keywords",
"bounds",
"extensions"
})
@XmlRootElement(name = "metadata")
public class MetadataType {

protected String name = "";
protected String desc = "";
protected PersonType author;
protected CopyrightType copyright = new CopyrightType();
protected List<LinkType> link = new ArrayList<LinkType>();
@XmlSchemaType(name = "Date")
protected Date time;
protected String keywords = "";
protected BoundsType bounds = new BoundsType();
protected ExtensionsType extensions = new ExtensionsType();
[...]
}

is it possible to unmarshal from an XML subtree; and if yes, what am I doing wrong?

UPDATE/1: thanks to lexicore, I'm a step further: element definitely contains the metadata node, @XmlRootElement is set, unmarshaling now with unmarshal(element, MetadataType.class) .

unmarshaling works, but the content of the objects is empty. I wonder if I run into some namespace troubles here?

the package com.topografix.gpx._1._1 contains a package-info.java, generated by xjc:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.topografix.com/GPX/1/1", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.topografix.gpx._1._1;

here's one of the test files:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" 
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" 
xmlns:wptx1="http://www.garmin.com/xmlschemas/WaypointExtension/v1" 
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" 
creator="GPSMAP 62s" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 
http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas /GpxExtensions/v3 
http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 
http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 
http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">
<metadata>
<link href="http://www.garmin.com">
<text>Garmin International</text>
</link>
<time>2014-01-01T22:26:49Z</time>
</metadata>
[...]

UPDATE/2: the namespace/package name is right. If the namespace is set to (ie) "foobar", the following exception is thrown: javax.xml.bind.JAXBException: Provider com.sun.xml.internal.bind.v2.ContextFactory could not be instantiated: javax.xml.bind.JAXBException: "foobar" doesnt contain ObjectFactory.class or jaxb.index

when initialized as before, the exception is not thrown, meaning that the namespace is correct AND the ObjectFactory.class is found.

String contextPath = MetadataType.class.getPackage().getName(); JAXBContext context = JAXBContext.newInstance(contextPath);

So, the namespace is correct, but somehow the "link" to the MetadataType class is missing?

I guess you have a few problems here:

  • Your MetadataType does not have the @XmlRootElement . So JAXB does not know which element is supposed to match it.
  • You want to unmarshal partially, but you're unmarshalling (I guess) the whole document.

What you could try:

  • Try unmarshalling a specific class from the specific node using the unmarshal(node, class) method
  • From your stack trace, you try to unmarshal gpx:gpx element, not the metadata element. You have to go deeper
  • You have to overtake package-info.java as well (or provide namespaces in your MetadataType , otheriwse you're missing namespaces
  • See this answer about partial unmarshalling .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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