8

I am trying to do the opposite of: JAXB - Ignore element

That is I'm trying to parse XML with JAXB to a POJO and I want to JAXB to fail if it does NOT find a matching Java property for a given element. I know I could probably do schema validation but I would rather not do that.

Example:

@XmlRootElement(name="Dog")
public class Dog {
    private String name;
    public String getName() {return name;}
    public String setName(String name) {this.name = name};
}

This XML would fail:

<Dog>
<id>blah</id>
<name>Stuff</name>
</Dog>

Because there is no id Java property

Community
  • 1
  • 1
Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • I don't know if you can achieve that using jaxb. However, this is the default behavior of XStream. – tibtof May 23 '12 at 14:00
  • Its more for unit testing purposes. I'll probably just have to use XmlUnit, unmarshal the XML and then remarshal it and compare which is what I have done in the past. – Adam Gent May 23 '12 at 14:04

2 Answers2

6

You can leverage a ValidationEventHandler to get the behaviour you are looking for. An unexpected elements is treated as a warning which is ignored by default. To support your use case you can supply a ValdidationEventHandler that fails on warnings (see below).

Demo

package forum10721257;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Dog.class);

        File xml = new File("src/forum10721257/input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setEventHandler(new ValidationEventHandler () {

            @Override
            public boolean handleEvent(ValidationEvent event) {
                return false;
            }

        });
        Dog dog = (Dog) unmarshaller.unmarshal(xml);
    }

}

JAXB RI Exception

Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"id"). Expected elements are <{}name>
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:243)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:238)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:105)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.childElement(Loader.java:90)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StructureLoader.childElement(StructureLoader.java:236)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:483)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:465)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:135)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:506)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:376)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2715)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:488)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:202)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:174)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:162)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:171)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:189)
    at forum10721257.Demo.main(Demo.java:26)

EclipseLink JAXB (MOXy)

We recently added this behaviour to MOXy. You will need to use EclipseLink 2.4.0 or 2.3.3. A nightly build can be obtained from the following location:


UPDATE

I needed to tweak the Dog class from your question. Below is what I used.

Dog

package forum10721257;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="Dog")
public class Dog {
    private String name;
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 1
    Hmm I tried that and it didn't work (granted it wasn't MOXy). I'll have to try it again. BTW I just used your trick for dynamic XML Elements: http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html . You get around when it comes to JAXB :) – Adam Gent May 23 '12 at 17:53
  • I ran this exact code in JDK 1.7.0 (for Mac) to get the exception I posted. Which version of the JDK are you using? – bdoughan May 23 '12 at 18:02
  • JDK 1.6 I'll try it again shortly. I think It was because the way I was making the context. – Adam Gent May 23 '12 at 18:16
0

The only way I have been able to validate missing Java properties (w/o schema) is to unmarshal the XML to my POJO, then remarshal it to XML using the same POJO and compare.

Below is an Example using XMLUnit which helps ignore order and whitespace issues.

@Test
public void testSerialization() throws Exception {
    XMLUnit.setIgnoreWhitespace(true);

    EventSearchResults r = 
            JAXB.unmarshal(getClass().getResourceAsStream("/EventSearchResults.xml"), 
                    EventSearchResults.class);
    StringWriter sw = new StringWriter();
    JAXB.marshal(r, sw);
    String expected = 
            IOUtils.toString(getClass().getResourceAsStream("/EventSearchResults.xml"), "UTF-8");
    String actual = sw.toString();
    //System.out.println(expected);
    System.out.println(actual);
    assertXMLEqual(expected, actual);
}

This is not ideal but for the most part works for Unit Testing.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203