JAXB @XmlValue deserializing not working with records
@XmlAttribute and @XmlElement seem to work fine with records, and @XmlValue seems to work when serializing but deserializing fails with java.lang.IllegalAccessException
public class Main {
public record TestObject (
@XmlValue
String name,
@XmlAttribute
int age) {}
public static void main(String[] args) throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
JakartaXmlBindAnnotationModule module = new JakartaXmlBindAnnotationModule();
xmlMapper.registerModule(module);
TestObject testObject = new TestObject("foo", 12);
String xml = xmlMapper.writeValueAsString(testObject);
TestObject testObject1 = xmlMapper.readValue(xml, TestObject.class);
}
}
stacktrace:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.lang.String field org.example.Main$TestObject.name to java.lang.String
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:276)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:627)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:615)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:638)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:193)
at com.fasterxml.jackson.databind.deser.impl.PropertyValue$Regular.assign(PropertyValue.java:60)
at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:211)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:519)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.dataformat.xml.deser.XmlTextDeserializer.deserialize(XmlTextDeserializer.java:96)
at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4730)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3677)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3645)
at org.example.Main.main(Main.java:25)
Caused by: java.lang.IllegalAccessException: Can not set final java.lang.String field org.example.Main$TestObject.name to java.lang.String
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:79)
at java.base/java.lang.reflect.Field.set(Field.java:799)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:190)
... 12 more
I'm using java 17 and jackson 2.14.1
I suspect this is related to general issues with annotation merging, records, to be resolved in jackson-databind in (I hope) nearish future (for 2.15).
One challenge here is that it could also be related to XML module handling. It would be great if the issue could be reproduced with Jackson's own annotations, only because then issue could be moved to jackson-dataformat-xml; test in this repo cannot depend on XML module.
Actually, I think I'll move this to XML module repo since it can depend on JAXB/Jakarta-Bind annotations but not vice versa.
Come to think of it, challenging to test even here wrt Records being JDK 15+ feature. Oh well.
I have a similar problem when adding the attribute name to @XmlElement. Using the same main-Method as above, the following works:
public record TestObject(
@XmlElement String name,
@XmlAttribute int age) {
}
But this does not:
public record TestObject(
@XmlElement(name = "Name") String name,
@XmlAttribute int age) {
}
Stacktrace:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.lang.String field org.example.Main$TestObject.name to java.lang.String at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:276) at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:627) at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:615) at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:638) at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:193) at com.fasterxml.jackson.databind.deser.impl.PropertyValue$Regular.assign(PropertyValue.java:60) at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:211) at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:519) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185) at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4730) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3677) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3645) at org.example.Main.main(Main.java:28) Caused by: java.lang.IllegalAccessException: Can not set final java.lang.String field org.example.Main$TestObject.name to java.lang.String at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76) at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80) at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:79) at java.base/java.lang.reflect.Field.set(Field.java:799) at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:190) ... 11 more
I'm using java 17 and jackson 2.14.2
I would recommend trying this against new 2.15.0-rc1 -- 2.15 branch has lots of improvements to handling of Record types.
@cowtowncoder Hey, I tried 2.15.0-rc1.
I can confirm that the code posted by @toellrich does indeed work with 2.15.0-rc1. However there seems to still be problems with the jakarta.xml.bind.annotation.XmlValue annotation.
With the code in the first post, serialization still works but deserializationfails with error:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class com.example.demo.Test$TestObject), not marked as ignorable (2 known properties: "name", "age"])
at [Source: (StringReader); line: 1, column: 38] (through reference chain: com.example.demo.Test$TestObject[""])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1138)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:2224)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1709)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperties(BeanDeserializerBase.java:1659)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:544)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1409)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4730)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3677)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3645)
Ok thank you for checking @micopiira. At least things get bit further.
Reference to empty String is because value marked with @XmlValue does not have actual property name matched from XML so it needs sort of placeholder.
Won't really help resolve the issue but may be interesting to know.