I want to serialize an immutable type both as json and as xml:
The serialized JSON is like:
{
"text" : "... the text..."
}
and the serialized xml is like:
<asText>_text_</asText>
(note that the text is the xml's element text)
The java object is like:
@JsonRootName("asText")
@Accessors(prefix="_")
public static class AsText {
@JsonProperty("text") @JacksonXmlText
@Getter private final String _text;
public AsText(@JsonProperty("text") final String text) {
_text = text;
}
}
beware that the _text property is final (so the object is immutable) and it's annotated with @JacksonXmlText
in order to be serialized as the xml element's text
Being the object immutable, a constructor from the text is needed and the constructor's argument must be annotated with @JsonProperty
public AsText(@JsonProperty("text") final String text) {
_text = text;
}
When serializing / deserializing to/from JSON everything works fine ... the problem arises when serializing / deserializing to/from XML:
// create the object
AsText obj = new AsText("_text_");
// init the mapper
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
log.warn("Serialized Xml\n{}",xml);
// Read from xml
log.warn("Read from Xml:");
AsText objReadedFromXml = mapper.readValue(xml,
AsText.class);
log.warn("Obj readed from serialized xml: {}",
objReadedFromXml.getClass().getName());
The exception is:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class r01f.types.url.UrlQueryStringParam), not marked as ignorable (2 known properties: "value", "name"])
It seems that the xml module needs the object's constructor to be annotated like:
public AsText(@JsonProperty("") final String text) {
_text = text;
}
BUT this does NOT even works:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `test.types.SerializeAsXmlElementTextTest$AsText` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
The annotation @JsonProperty("text")
at the constructor's argument is needed to deserialize from JSON
... how can i make this to work
Try adding a public getter for the property. I believe that should fix the deserialization issue.
@JsonRootName("asText")
@Accessors(prefix = "_")
public static class AsText {
@JsonProperty("text")
@JacksonXmlText
@Getter
private final String _text;
public AsText(@JsonProperty("text") final String text) {
_text = text;
}
public String getText() {
return _text;
}
}
Actually, it works without adding a getter too, with these versions of Lombak & Jackson.
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
</dependencies>
I had same issue with error "no delegate- or property-based Creator". In my case it was problem with Immutables version 2.5.6. I have fixed it by downgrade to version 2.5.5. Version 2.5.6 is available in mvnrepository.com but on official page is as stable version marked 2.5.5.
Update 2018
This hack worked in 2.9.0 but it seems to stop working after this commit. It is not clear if there is an easy way to make it work again.
It looks like the main reason for your issue is that you try to use JSON and XML serialization at the same time but with different configurations. Unfortunately XmlMapper
inherits from ObjectMapper
and inherits all the JSON-specific configuration (and you can override it but can not clear it with XML-specific annotations) which is the reason for your conflict. It seems that the simplest way to work this around is to use @JsonAlias
annotation in the constructor. It is a bit hacky but it works. Here is a minimal example (without Lombok) that works for me:
@JsonRootName("asText")
public static class AsText {
@JsonProperty("text")
@JacksonXmlText
private final String _text;
public AsText(@JsonAlias("") @JsonProperty("text") final String text) {
_text = text;
}
@JsonIgnore
public String getText() {
return _text;
}
@Override
public String toString() {
return "AsText{" +
"_text='" + _text + '\'' +
'}';
}
}
Note that I also added @JsonIgnore
to the getter because else I didn't get XML format you requested (and you can do the same using Lombok's onMethod
as described at onX).
For a simple test:
public static void main(String[] args) throws IOException {
// create the object
AsText obj = new AsText("123_text_");
// init the mapper
//ObjectMapper mapper = new ObjectMapper();
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
System.out.println("Serialized Xml\n" + xml);
// Read from xml
AsText objReadedFromXml = mapper.readValue(xml, AsText.class);
System.out.println("Read from Xml: " + objReadedFromXml);
}
I get the following output:
Serialized Xml
<asText>123_text_</asText>
Read from Xml: AsText{_text='123_text_'}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With