Arnab Biswas
Arnab Biswas

Reputation: 4605

JAXB unmarshalling of invalid integer

In our product we use apache CXF. Because of performance restriction, the schema validation has been set to false. Now, for an integer element if I am providing an invalid value, JAXB is unmarshaling it to some thing else. For example,

9999999999 is converted into 1410065407.

988888888888 is converted into 1046410808.

My question is what is the logic (formula) that is being followed here?

How to handle this (Considering the validation will be off)? I want such a value to be rejected.

Upvotes: 4

Views: 5491

Answers (1)

bdoughan
bdoughan

Reputation: 149057

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

SHORT ANSWER

This appears to be a bug in the JAXB reference implementation. I would recommend entering a bug for it at the following location:

This same use case works correctly in EclipseLink JAXB (MOXy).


LONG ANSWER

Below is a complete example that demonstrates the issue:

Root

Below is a domain class with int and integer fields.

package forum13216624;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    int int1;
    int int2;
    Integer integer1;
    Integer integer2;

}

input.xml

Below is an XML document with the values from your question.

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <int1>9999999999</int1>
    <int2>988888888888</int2>
    <integer1>9999999999</integer1>
    <integer2>988888888888</integer2>
</root>

Demo

In the demo code below I've specified a ValidationEventHandler on the Unmarshaller. This should catch a ValidationEvent for any invalid values encountered during an unmarhsal operation.

package forum13216624;

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

public class Demo {

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

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setEventHandler(new ValidationEventHandler() {

            @Override
            public boolean handleEvent(ValidationEvent event) {
                System.out.println(event.getMessage());
                return true;
            }}

        );
        File xml = new File("src/forum13216624/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        System.out.println(root.int1);
        System.out.println(root.int2);
        System.out.println(root.integer1);
        System.out.println(root.integer2);
    }

}

Output - JAXB Reference Implementation

This output matches the behaviour you are seeing.

1410065407
1046410808
1410065407
1046410808

Output - EclipseLink JAXB (MOXy)

If you specify MOXy as your JAXB provider (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html) this use case works as expected. You will get a ValidationEvent for each invalid value, and if the ValidationEvent is handled the field/property won't be set to a invalid value.

Exception Description: The object [9999999999], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[int1-->int1/text()]] with descriptor [XMLDescriptor(forum13216624.Root --> [DatabaseTable(root)])], could not be converted to [class java.lang.Integer].
Internal Exception: java.lang.NumberFormatException: For input string: "9999999999"

Exception Description: The object [988888888888], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[int2-->int2/text()]] with descriptor [XMLDescriptor(forum13216624.Root --> [DatabaseTable(root)])], could not be converted to [class java.lang.Integer].
Internal Exception: java.lang.NumberFormatException: For input string: "988888888888"

Exception Description: The object [9999999999], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[integer1-->integer1/text()]] with descriptor [XMLDescriptor(forum13216624.Root --> [DatabaseTable(root)])], could not be converted to [class java.lang.Integer].
Internal Exception: java.lang.NumberFormatException: For input string: "9999999999"

Exception Description: The object [988888888888], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[integer2-->integer2/text()]] with descriptor [XMLDescriptor(forum13216624.Root --> [DatabaseTable(root)])], could not be converted to [class java.lang.Integer].
Internal Exception: java.lang.NumberFormatException: For input string: "988888888888"
0
0
null
null

POTENTIAL WORKAROUND

If your fields/properties are of type Integer and you can switch from the JAXB reference implementation then you could create an XmlAdapter to do your own Integer to/from String conversions.

IntegerAdapter

Below is an example of an XmlAdapter demonstrating how you could provide your own conversion logic.

package forum13216624;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class IntegerAdapter extends XmlAdapter<String, Integer>{

    @Override
    public Integer unmarshal(String string) throws Exception {
        return Integer.valueOf(string);
    }

    @Override
    public String marshal(Integer integer) throws Exception {
        return String.valueOf(integer);
    }

}

package-info

Using the @XmlJavaTypeAdapter annotation at the package level means that the XmlAdapter will apply to all fields/properties of type Integer for classes in this package.

@XmlJavaTypeAdapter(value=IntegerAdapter.class, type=Integer.class)
package forum13216624;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

Output

Below is the updated output when the RI is used. The int values are still wrong, but not the Integer values are null and ValidationEvents are produced as expected.

java.lang.NumberFormatException: For input string: "9999999999"
java.lang.NumberFormatException: For input string: "988888888888"
1410065407
1046410808
null
null

For More Information

Upvotes: 7

Related Questions