I'd like to have different name for my element when it's serialized to XML (for example "fooXml") and different for JSON (for example "fooJson"). Is it possible?
I'm using XML annotations like:
#XmlElements({
#XmlElement(type = Foo.class, name = "fooXml"),
})
private SortedSet<Foo> fooSet;
I've tried already #JsonProperty, with without any luck.
I've also tried exporting it to getter method, like:
#XmlElement(type = Foo.class, name = "fooXml")
#JsonProperty(value = "fooJson")
public List<Foo> getFooList() {
return new ArrayList<>(fooSet);
}
But it's always ignoring JSON annotations and serializing to XML form (fooXml name).
How shall I do it?
edit: I'm using Jersey-json.
Alright, I had a need for this same functionality and found a solution that works for this:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
#JsonProperty("MyJsonName")
#JacksonXmlProperty(localName = "MyXmlName")
private MyProperty myProperty;
Works for me, and myProperty will be in the 'MyJsonName' field in Json and the 'MyXmlName' in XML.
Related
I'm extending code from an existing Java class that serializes to and from XML. The existing class is somewhat like this:
#Getter
#JacksonXmlRootElement("element")
public class Element {
#JacksonXmlProperty(localName = "type", isAttribute = true)
private String type;
}
The type field has a finite set of possible values so I created an enum Type with all possible values and (to avoid breaking existing functionality) added a new field to the class, like so:
#Getter
#JacksonXmlRootElement("element")
public class Element {
#JacksonXmlProperty(localName = "type", isAttribute = true)
private String type;
#JacksonXmlProperty(localName = "type", isAttribute = true)
#JsonDeserialize(using = TypeDeserializer.class)
private Type typeEnum;
}
This gives me the following error:
Multiple fields representing property "type": Element#type vs Element#typeEnum
I understand why this is a problem cuz when Jackson would try to serialize the class, two fields in my class map onto the same field in the output XML.
I tried adding a #JsonIgnore on one of the fields and it gets rid of the error but has the side effect of not populating the ignored field either. Is there a way to annotate that a field should be deserialized (while reading XML) but not serialized (while writing XML)?
I really need to keep both fields in the class to not disturb any legacy code that might be using the first field, but at the same time allow newer code to leverage the second field.
Thank you!
Thank you for taking the time to read.
My goal is to deserialize the response from an API request into 2 usable java objects.
I am sending an POST request to an endpoint to create a job in our schedule. The job is created successfully and the following XML is returned in the body:
<entry xmlns="http://purl.org/atom/ns#">
<id>0</id>
<title>Job has been created.</title>
<source>com.tidalsoft.framework.rpc.Result</source>
<tes:result xmlns:tes="http://www.auto-schedule.com/client">
<tes:message>Job has been created.</tes:message>
<tes:objectid>42320</tes:objectid>
<tes:id>0</tes:id>
<tes:operation>CREATE</tes:operation>
<tes:ok>true</tes:ok>
<tes:objectname>Job</tes:objectname>
</tes:result>
</entry>
When I attempt to only capture the elements id, title, and source the data is captured successfully. The problem is when I introduce the child object, which attempts to capture the data in the tes:result element.
Here's what the parent POJO looks like:
#XmlRootElement(name = "entry")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
private String id;
private String title;
private String source;
ResponseDetails result;
public Response() {}
}
and here is the child object:
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseDetails {
#XmlElement(name = "tes:message")
String message;
#XmlElement(name = "tes:objectid")
String objectid;
#XmlElement(name = "tes:operation")
String operation;
#XmlElement(name = "tes:ok")
String ok;
#XmlElement(name = "tes:objectname")
String objectname;
}
Finally, here is the package-info.java file I am using:
#XmlSchema(
namespace = "http://purl.org/atom/ns#",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.netspend.raven.tidal.request.response;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Any ideas are greatly appreciated. Thanks.
The problem is: Your Java code doesn't correctly specify the namespaces
corresponding to the inner XML element
<tes:result xmlns:tes="http://www.auto-schedule.com/client">
<tes:message>Job has been created.</tes:message>
<tes:objectid>42320</tes:objectid>
<tes:id>0</tes:id>
<tes:operation>CREATE</tes:operation>
<tes:ok>true</tes:ok>
<tes:objectname>Job</tes:objectname>
</tes:result>
The <tes:result> XML element has the namespace "http://www.auto-schedule.com/client".
The namespace prefix tes: itself is irrelevant for Java.
(XML namespace prefixes were invented only for better readability of the XML code.)
Therefore in your Response class, instead of just writing
ResponseDetails result;
you need to write
#XmlElement(namespace = "http://www.auto-schedule.com/client")
ResponseDetails result;
See also the javadoc of XmlElement.namespace.
Also all the XML sub-elements within the <tes:result> are specified
with this namespace "http://www.auto-schedule.com/client".
Therefore, also inside your ResponseDetails you have to correct
the namespace stuff. For example, instead of
#XmlElement(name = "tes:message")
String message;
you need to write
#XmlElement(name = "message", namespace = "http://www.auto-schedule.com/client")
String message;
You may also omit the name = "message" and simply write
#XmlElement(namespace = "http://www.auto-schedule.com/client")
String message;
because JAXB picks up this name from your Java property name message.
And similar for all the other properties in this class.
I have an issue with generating a XML string with the Java Jackson XmlMapper: It generates the wrong order of entries in the XML string, nevertheless I use #JsonPropertyOrder and the members are in the needed order inside the class.
Please see my code:
#JsonPropertyOrder({ "craneNumber", "moveType", "reference", "unitNumber", "ISOCode", "IMOLabels", "seal", "doorDirection" })
public class OcrDataResultUnit {
#JacksonXmlElementWrapper(localName="unit")
private String craneNumber;
private String moveType;
private String reference;
private String unitNumber;
#JsonProperty("ISOCode")
private String isoCode;
#JacksonXmlElementWrapper(localName="IMOLabels")
#JsonProperty("DGSIMOClass")
private List<String> imoLabels = new ArrayList<>();
#JsonProperty("seal")
private String seal;
#JsonProperty("doorDirection")
private String doorDirection;
// all getters and setters ...
Usage:
XmlMapper mapper = new XmlMapper();
String msgXml = mapper.writeValueAsString(this);
Result:
<unit>
<craneNumber>QC01</craneNumber>
<moveType>D</moveType>
<reference>12345678901234567890123456789012</reference>
<unitNumber>ABCD00001234</unitNumber>
<ISOCode>22G1</ISOCode>
<seal>Y</seal>
<doorDirection>H</doorDirection>
<IMOLabels>
<DGSIMOClass>1.5</DGSIMOClass>
<DGSIMOClass>2.1</DGSIMOClass>
</IMOLabels>
</unit>
I get the same result without the #JsonProperty on the last to members. That was a try.
The structure is part of a bigger XML structure.
Also replaced #JsonProperty with #JacksonXmlProperty: Same result.
As far as I see #JsonPropertyOrder is correct to be used for XML as well.
Does anybody have an idea?
Maybe I am just blind - actually I hope so :-)
Thank you and best regards
In your JsonPropertyOrder annotation, the property is called "DGSIMOClass", not "IMOLabels". You should switch it out for the correct name.
My XML:
<body>
<type>authorizationStatus</type>
<data>
<AuthorizationStatusMessage>
<id>12345679</id>
<email>abc</email>
</AuthorizationStatusMessage>
</data>
</body>
I want to unmarshal this XML to a POJO like that:
public class XMPPMessage {
private String type;
private String data;
}
Jackson unmarshal gives me a HashMap:
{AuthorizationStatusMessage={id_colaborador=12345679, email=rhochman#atech.com}}
But I want keep the data inner XML as a String like that:
<AuthorizationStatusMessage><id>12345679</id><email>abc</email></AuthorizationStatusMessage>
How can I keep the inner XML as a String??
I can see two options.
If you can modify your XML schema, you might want to leverage CDATA section (https://en.wikipedia.org/wiki/CDATA) to keep your <data> contents as String.
You can implement a custom Jackson deserializer to extract type and data only. There are plenty of good references on the web (e.g. http://www.baeldung.com/jackson-deserialization).
I got it someway... I found a library named Conversion Box to convert HashMap to XML.
I create custom Deserializer for storing inner XML as String, example on Kotlin:
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.dataformat.xml.XmlMapper
object XmlInnerDeserializer : StdScalarDeserializer<String>(String::class.java) {
override fun deserialize(jp: JsonParser?, context: DeserializationContext?): String {
val node = jp!!.codec.readTree<ObjectNode>(jp)
val xmlMapper: ObjectMapper = XmlMapper()
val xml = xmlMapper.writeValueAsString(node)
return xml.toString()
}
}
How use it:
#JacksonXmlRootElement(localName = "body")
#JsonIgnoreProperties(ignoreUnknown = true)
class XMPPMessage (
#JsonDeserialize(using = XmlInnerDeserializer::class)
#JacksonXmlProperty(localName = "data")
val data: String
}
Field data contains "<ObjectNode><AuthorizationStatusMessage>... </AuthorizationStatusMessage></ObjectNode>" string.
By no means am I a Jackon/JSON wizard, which is probably evident from the following issue I'm running into:
I have 2 possible data structures I'm receiving.
The first one is called amountTransaction:
{
"amountTransaction": {
"clientCorrelator":"54321",
"endUserId":"tel:+16309700001"
}
}
Which is represented by the following Java object:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonTypeName(value = "amountTransaction")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class AmountTransaction {
private String clientCorrelator;
private String endUserId;
...
}
However the amountTransaction object also appears as child element of the paymentTransactionNotification object:
{
"paymentTransactionNotification": {
"amountTransaction": {
"clientCorrelator": "54321",
"endUserId": "tel:+16309700001"
}
}
}
..which I thought would be represented by:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonTypeName(value = "paymentTransactionNotification")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class PaymentTransactionNotification {
private AmountTransaction amountTransaction;
...
}
Parsing the JSON with the amountTransaction object alone works fine. It's a pretty straightforward example of a WRAPPER_OBJECT.
However when trying to parse the JSON for the paymentTransactionNotification, I'm getting an exception indicating that it can't properly deal with the amountTransaction as element of the paymentTransactionNotification:
com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id 'clientCorrelator' into a subtype of [simple type, class com.sf.oneapi.pojos.AmountTransaction]
Any thoughts on how I can properly annotate this so my code can properly deal with both stand alone, as well as encapsulated amountTransaction objects?
By default wrapping root node in Jackson is disabled. You can wrap inner objects but if you want to wrap root node you need to enable jackson feature for it (https://jira.codehaus.org/browse/JACKSON-747):
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationConfig.Feature.WRAP_ROOT_VALUE);
objectMapper.enable(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
When you enabled these features you already said Jackson to wrap the root element and you don't need #JsonTypeInfo and #JsonTypeName anymore. You can simple delete them. But now you need to customize the root node name and you can use #JsonRootName for it. Your classes should look like this:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonRootName("amountTransaction")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class AmountTransaction {
private String clientCorrelator;
private String endUserId;
...............
}
And
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonRootName("paymentTransactionNotification")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class PaymentTransactionNotification {
private AmountTransaction amountTransaction;
.............
}
I've tried and Jackson converted both JSON requests as expected.