As of version 2.16.1, infinite values of float and double are serialized in a way that is incompatible with the XML Schema definition and JAXB. Specifically, jackson-dataformat-xml serializes these values as the strings Infinity or -Infinity. XML Schema, however, says they should be serialized as INF or -INF, and that is what JAXB does.
Example program (click to show)
package org.example;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class Main {
public static void main(String[] args) throws IOException {
ExampleObject original, deserialized;
String serialized;
original = new ExampleObject();
original.x = Double.POSITIVE_INFINITY;
original.y = Double.NEGATIVE_INFINITY;
original.z = Double.NaN;
original.fx = Float.POSITIVE_INFINITY;
original.fy = Float.NEGATIVE_INFINITY;
original.fz = Float.NaN;
System.out.println("--- Jackson serialization ---");
serialized = serializeWithJackson(original);
System.out.println(serialized);
System.out.println("--- Jackson deserialization ---");
deserialized = deserializeWithJackson(serialized);
System.out.println(deserialized);
System.out.println("--- JAXB serialization ---");
serialized = serializeWithJaxb(original);
System.out.println(serialized);
System.out.println("--- JAXB deserialization ---");
deserialized = deserializeWithJaxb(serialized);
System.out.println(deserialized);
System.out.println("--- serialized with JAXB, deserialized with Jackson ---");
deserialized = deserializeWithJackson(serialized);
System.out.println(deserialized);
System.out.println("--- serialized with Jackson, deserialized with JAXB ---");
serialized = serializeWithJackson(original);
deserialized = deserializeWithJaxb(serialized);
System.out.println(deserialized);
}
private static String serializeWithJackson(ExampleObject object) throws IOException {
var buf = new StringWriter();
new XmlMapper().writeValue(buf, object);
return buf.toString();
}
private static ExampleObject deserializeWithJackson(String xml) throws JsonProcessingException {
return new XmlMapper().readValue(xml, ExampleObject.class);
}
private static String serializeWithJaxb(ExampleObject object) {
var buf = new StringWriter();
JAXB.marshal(object, buf);
return buf.toString();
}
private static ExampleObject deserializeWithJaxb(String xml) {
return JAXB.unmarshal(new StringReader(xml), ExampleObject.class);
}
}
@XmlRootElement(name = "example")
class ExampleObject {
@XmlElement
public double x, y, z;
@XmlElement
public float fx, fy, fz;
@Override
public String toString() {
return String.format("x=%f y=%f z=%f fx=%f fy=%f fz=%f", x, y, z, fx, fy, fz);
}
}
Maven POM for example program (click to show)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>jackson-xml-double</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
</project>
Output from example program (click to show)
--- Jackson serialization ---
<ExampleObject><x>Infinity</x><y>-Infinity</y><z>NaN</z><fx>Infinity</fx><fy>-Infinity</fy><fz>NaN</fz></ExampleObject>
--- Jackson deserialization ---
x=Infinity y=-Infinity z=NaN fx=Infinity fy=-Infinity fz=NaN
--- JAXB serialization ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<example>
<x>INF</x>
<y>-INF</y>
<z>NaN</z>
<fx>INF</fx>
<fy>-INF</fy>
<fz>NaN</fz>
</example>
--- JAXB deserialization ---
x=Infinity y=-Infinity z=NaN fx=Infinity fy=-Infinity fz=NaN
--- serialized with JAXB, deserialized with Jackson ---
x=Infinity y=-Infinity z=NaN fx=Infinity fy=-Infinity fz=NaN
--- serialized with Jackson, deserialized with JAXB ---
x=0.000000 y=0.000000 z=NaN fx=0.000000 fy=0.000000 fz=NaN
As the example program's output shows, Jackson understands both its own format and the XML Schema format for floating-point infinity. JAXB, however, understands only the XML Schema format, and fails to parse Jackson's format.
The problem seems to be that jackson-dataformat-xml calls TypedXMLStreamWriter methods to serialize floating-point values, which ultimately uses NumberUtil.write{Float,Double} from StAX2, which in turn uses java.lang.String.valueOf to serialize the number, without any special handling of infinity.
Deserialization of XML Schema-formatted numbers seems to work correctly. Only serialization has an issue.
This issue only affects positive and negative infinity. java.lang.String.valueOf differs from XML Schema only in how it represents infinity; it uses the same format as XML Schema for NaN and finite values.
As of version 2.16.1, infinite values of
floatanddoubleare serialized in a way that is incompatible with the XML Schema definition and JAXB. Specifically, jackson-dataformat-xml serializes these values as the stringsInfinityor-Infinity. XML Schema, however, says they should be serialized asINFor-INF, and that is what JAXB does.Example program (click to show)
Maven POM for example program (click to show)
Output from example program (click to show)
As the example program's output shows, Jackson understands both its own format and the XML Schema format for floating-point infinity. JAXB, however, understands only the XML Schema format, and fails to parse Jackson's format.
The problem seems to be that jackson-dataformat-xml calls
TypedXMLStreamWritermethods to serialize floating-point values, which ultimately usesNumberUtil.write{Float,Double}from StAX2, which in turn usesjava.lang.String.valueOfto serialize the number, without any special handling of infinity.Deserialization of XML Schema-formatted numbers seems to work correctly. Only serialization has an issue.
This issue only affects positive and negative infinity.
java.lang.String.valueOfdiffers from XML Schema only in how it represents infinity; it uses the same format as XML Schema for NaN and finite values.