Unmarshalling json by reversing the affect of #XmlElement renaming? - java

I have a class defined as follows:
public class Contact implements Serializable
{
private final static long serialVersionUID = 1L;
#XmlElement(name = "last-name", required = true)
protected String lastName;
#XmlElement(name = "first-name", required = true)
protected String firstName;
#XmlElement(required = true)
protected String id;
#XmlElement(name = "primary-phone")
protected String primaryPhone;
#XmlElement(name = "cellular-phone")
protected String cellularPhone;
}
This class is being used to generate marshalled JSON versions which are communicated over the internet. On the receiving end I'm trying to unmarshall the JSON, but I'm having difficulty because of the difference in naming, i.e. for example the unmarshalling library expects a variable named primaryPhone, not primary-phone which is what I have at the receiving end.
Besides pre-processing the received JSON text to manually replace instances of primary-phone with primaryPhone, is there some other more automated way to avoid this problem ? The problem with manually converting strings is that tomorrow if the Class definition changes, the code I'm writing will also need to be updated.
Here's a code snippet showing what I'm currently doing without any manually string conversion:
String contact = "\"last-name\": \"ahmadka\"";
ObjectMapper objMapper = new ObjectMapper();
Contact cObj = objMapper.readValue(contact, Contact.class);
But with the above code I get an exception on the last line reading this:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "last-name" (class Contact), not marked as ignorable (5 known properties: "lastName", "cellularPhone", "id", "primaryPhone", "firstName", ])
at ...........//rest of the stack

Jackson by default doesn't know about JAXB annotations (i.e. #XmlRootElement). It needs to be configured with an external module in order to have this capability. On the server, you most likely have this without even knowing it.
On the client side if you want to configure the ObjectMapper, then you need to add the following module:
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${jackson2.version}</version>
</dependency>
Then just register the JAXB annotations module.
ObjectMapper objMapper = new ObjectMapper();
mapper.registerModule(new JaxbAnnotationModule());

Related

Java :: JacksonXmlProperty :: Multiple fields with same name

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!

Jackson XmlMapper generates wrong XML entries order

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.

Issue with deserializing JSON using Jackson

I have such JSON
{"body":{"result":[{"crossStateId":1,"raceId":181564,"withOfficer":1,"documents":[{"indexed":0,"documentNumber":"zzz","isMain":1,"documentTypeId":6,"serverId":16,"countryId":327,"useDate":"2017-02-07T19:31:51.000+0000","documentSubTypeId":6,"crossId":5018177,"documentId":44973231,"personId":222,"infinity":0,"documentValid":"2023-08-25T20:00:00.000+0000"}],"directionId":2,"documentNumber":"sss","operatorUsername":"AIRPORT_84","crossDate":"2017-02-07T19:31:51.000+0000","serverId":16,"crossTypeId":1,"crossRegisterDate":"2017-02-07T19:31:52.818+0000","officerNote":"","children":[],"personNote":"","crossId":5018177,"workplaceId":82,"divisionId":2,"race":{"carriageContainer":0,"raceId":181564,"raceStateId":1,"directionId":2,"creatorId":415,"countryId":327,"transportIdByType":605,"raceDateTime":"2017-02-07T19:20:58.000+0000","raceNumber":"841 sss sss","creatorUsername":"AIRPORT_8","divisionId":2,"transportTypeId":3,"createDate":"2017-02-07T19:20:58.000+0000"},"syncState":0,"autos":[],"userId":491,"raceNumber":"841 sss sss","operatorNote":"","person":{"firstNameEn":"JUMBERI","indexed":1,"lastNameGe":"ჩოხელი","genderId":2,"personId":6027803,"personalNumber":"222","countryNameGe":"sss","birthDate":"1963-06-14T20:00:00.000+0000","lastNameEn":"sss","countryId":327,"firstNameGe":"sss"},"airplane":{"raceNumber":"841 sss sss","airCompanyId":1,"airplaneId":605,"airportId":5657,"bortNumber":"01","transportSubTypeId":78,"countryId":360},"underAge":0,"personId":6027803,"decisionId":22}],"total":8264},"errorCode":0}
I would like to deserialize it to Java class but I am interested in only some JSON fields. Anyway here are the model classes:
public class Response implements Serializable {
private Body body;
private long errorCode;
}
public class Body implements Serializable {
Result result[];
}
public class Result implements Serializable {
private long crossStateId;
private long raceId;
private Person person;
private Child children [];
private Auto autos[];
}
etc.
But for some reason I get following exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "body" (Class com.demo.Response), not marked as
ignorable at [Source: java.io.StringReader#6483f5ae; line: 1, column:
10] (through reference chain: com.demo.Response["body"])
Here is code(the JSON string is correctly received and has same format as I initially mentioned in the beginning):
String res = MainProgram.sendGet("someURL");
ObjectMapper objectMapper = new ObjectMapper();
Response ob = objectMapper.readValue(res, Response.class);
I would appreciate some help.
You need to create getters and setters for the fields, and you should add annotations to your fields.
Annotation:
#JsonProperty(value = "body")
private Body body;
Doing one of above will make it work.
Sidenote:
You can create your pojos from json automatically with http://www.jsonschema2pojo.org/. Just paste it in and download it, or use one of their plugins.
As mentioned by others, private fields are not auto-detect by default, so either:
Annotating fields with #JsonProperty OR
Adding setter
is needed for deserialization.
However, there is another possibility: you can use annotations #JsonAutoDetect to change minimum visibility needed, and here enable discovery of ALL fields.
Or you can even change the defaults used via ObjectMapper method (something like setVisibility(...)).

JPA / Jackson - Exclude fields when deserialize and include them when serialize

I have a JPA entity with a couple of fields (the real ones are more complex). I'm receiving some data via REST (POST operation in a Spring controller) and storing it right away in the JPA entities; I want to see if there is a possibility to exclude some field(s) when the request is sent, Jackson deserializes it, and constructs the object. But at the same time I want those fields to be included when I send back (object gets serialized) the response.
#Table("key_card")
public final class KeyCard {
private String username; // Don't want this to be sent as input,
// but want to be able to send it back
// in the response
#NotBlank
private final char[] password;
}
I'm just trying not to model it twice (for the request and response) if there is a way to solve this.
You can use JSON views: http://wiki.fasterxml.com/JacksonJsonView
Class Views {
static class AlwaysInclude { }
static class OnlyOnSerialize extends AlwaysInclude { }
}
And then on your view:
#Table("key_card")
public final class KeyCard {
#JsonView(Views.OnlyOnSerialize.class)
private String username;
#JsonView(Views.AlwaysInclude.class)
#NotBlank
private final char[] password;
}
To exclude a Java object property only from Json deserialization and to include instead its value during serialization you can use an appropriate combination of #JsonIgnore and #JsonProperty annotations.
In particular you should:
annotate with #JsonIgnore the property itself
annotate with #JsonIgnore its set method
annotate with #JsonProperty its get method
Here you can find an in-depth explanation and an example: Jackson: using #JsonIgnore and #JsonProperty annotations to exclude a property only from JSON deserialization

Hibernate and Jackson lazy serialization

I'm working on a project using Hibernate and Jackson to serialize my objects.
I think I understand how it is suposed to work but I can't manage to make it works.
If I understand well, as soon as a relation fetch mode is set to LAZY, if you want this relation, you have to initialize it.
Here is my class :
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#Table(schema="MDDI_ADMIN", name = "MINIUSINE")
#Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class MiniUsine {
#Id
#Column(name="MINIUSINEID", nullable = false)
private int miniUsineID;
#Column(name = "NAME", length = 40, nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="FluxID")
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private Set<Flux> fluxs = new HashSet<Flux>();
And all getters and setters.
I've also tried this JsonInclude.Include.NON_EMPTY as class annotation. Also tried the NON_NULL.
However, jackson keeps sending me
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: MiniUsine.fluxs, no session or session was closed (through reference chain: java.util.ArrayList[0]->MiniUsine["fluxs"])
I'm serializing it with : mapper.writeValueAsString(optMU);
Using Jackson 2.3.2
Thanks for help
I know this is an old question but I had the same problem.
You must add a new maven dependecy to support JSON serialization and deserialization of Hibernate. I used Hibernate5 so I added
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.2</version>
</dependency>
Now register the new module.
#Provider
public class JacksonHibernateProvider implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(final Class<?> type) {
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate5Module());
return mapper;
}
}
As far as I understand, the entity object that hibernate returns is a proxy which derives from your entity class. If you try to access getter methods for lazy fields outside of a transaction, you get LazyInitializationException.
The point I want to make is setting fluxs to empty set doesn't help you at all.
private Set<Flux> fluxs = new HashSet<Flux>();
Hibernate overloads the getter and if you try to access it outside of a transaction(which jackson is doing to check if it is empty), you get the LazyInit error.

Categories

Resources