Json Deserialize in multiple Java objects - java

I try to deserialize json :
{
"date": "2021_05",
"uuid": "3ba8b966-993f-49e0-b349-e528843a382c",
"dataset": "dataset",
"hmm_hit": "hit",
"hmm_evalue": "6.7e-186",
"hmm_score": "610.9"
},
I have two entities :
#Entity
public class HmmResult {
#Id
#GeneratedValue
#JsonIgnore
private Integer id;
#JsonProperty("hmm_hit")
private String hmm;
#JsonProperty("hmm_evalue")
private String eValue;
#JsonProperty("hmm_score")
private Float score;
}
and
#Entity
public class Protein {
#Id
#GeneratedValue
#JsonIgnore
private Integer id;
#JsonProperty("date")
private String date;
#JsonProperty("uuid")
private String uuid;
#JsonProperty("dataset")
private String dataset;
#OneToOne
#JsonDeserialize(as = HmmResult.class)
private HmmResult hmmResult;
How to deserialize both entities at the same time with one entry of json ?
Here is extract of main with Jackson ObjectMapper :
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Protein p = objectMapper.readValue(new File(file), Protein.class);
It parses "date", "uuid" and "dataset" fine but can not parse HmmResult object with "hmm_subfamily", "hmm_evalue" and "hmm_score" values : I get error : p.getHmmResult() : null. (HmmResult hm = objectMapper.readValue(new File(file), HmmResult.class); works fine too alone).

There is #JsonUnwrapped annotation for this. Should work like this:
public class Protein {
#JsonProperty("date")
private String date;
#JsonProperty("uuid")
private String uuid;
#JsonProperty("dataset")
private String dataset;
#JsonUnwrapped
private HmmResult hmmResult;
}
var protein = new ObjectMapper().readValue(new File(file), Protein.class);

Related

Post request transform int field to string automatically

I'm doing a dummy app of a hostpital. The problem I'm having is that, I'm trying to verify that when a Patient is created, the fields passed are of the correct type, but whenever I POST an Int in a String field, it doesn't fail and just transform the Int to String. The field I'm trying to make fail is "surname", which by the definition of the Patient class, is a String.
If I do this (I pass a number to the "surname" field):
{
"name": "John",
"surname": 43,
"sickness": "headache"
}
It just transforms 43 into a String by the time its in the Controller method.
Here we have the Patient object:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
public class Patient implements Serializable {
private static final long serialVersionUID = 4518011202924886996L;
#Id
//TODO: posible cambiar luego la generationType
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private Long id;
#Column(name = "patient_name")
#JsonProperty(required = true)
private String name;
#Column(name = "patient_surname")
#JsonProperty(required = true)
private String surname;
#Column(name = "patient_sickness")
#JsonProperty(required = true)
private String sickness;
}
And this is the controller class:
#Controller
#Path("/patient")
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public class PatientController {
#POST
#Path("")
public ResponseEntity<Object> postPatient(final Patient patient) {
ResponseEntity<Object> createdPatient = patientBusiness.createPatient(patient);
return new ResponseEntity<Patient>(createdPatient.getBody(), createdPatient.getStatusCode());
}
EDIT 1:
Following the "clues" and closing the circle of attention, I tried modifying the ObjectMapper, but my configuration isn't applying. I'm still getting the error from above.
This is the config class:
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper getModifiedObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false);
mapper.coercionConfigFor(LogicalType.Integer).setCoercion(CoercionInputShape.String, CoercionAction.Fail);
return mapper;
}
}
Even added the property to the application.yml, but still nothing:
spring:
jackson:
mapper:
allow-coercion-of-scalars: false
Any help is appreciated. Thx.
In the end I referred to this post to do a deserializer and a module to just have it along all the program, not just the field I want not to be transformed.
Disable conversion of scalars to strings when deserializing with Jackson

Jackson JSON serializer returns extra collection name

I have an list of things that I return as a jackson list. What I would like is this:
"things": { [
{
"id": "1234",
..list of students
but currently, I am getting this:
"things": {
"HashSet": [
{
"id": "1234",
I am using a JsonSerializer>, which is why it adds the HashSet field. I tried adding a json property on the field, but its not allowed since it is a local variable.
I am currently using the jackson library and have looked into:
Jackson annotation - How to rename element names?
How to rename root key in JSON serialization with Jackson
but they seem to have a different issue altogether. Any ideas? Thanks!
Edit:
Added the class implementations. Note that I call owner, that contains Things. Also, my jpa annotations are there as well. Thanks.
#Entity #Table(name = "owner")
public class Owner extends BaseEntity implements Persistence {
#Column
private String name;
#Column
private String description;
#Column
private Integer capacity;
#Column
#JsonSerialize(using = ThingSerializer.class)
#JsonProperty("things")
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "owners")
private Set<Thing> tihngs = new HashSet<>();
public class ThingSerializer extends JsonSerializer<Set<Thing>> {
#Override public void serialize(Set<Thing> thingSet, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws
IOException,
JsonProcessingException {
Set<Thing> things = new HashSet<>();
thingSet.forEach(thing -> {
Thing t = new Thing();
t.setCreatedBy(thing.getCreatedBy());
t.setCreationDate(thing.getCreationDate());
t.setId(thing.getId());
t.setDateModified(thing.getDateModified());
t.setModifiedBy(thing.getModifiedBy());
t.setStatus(thing.getStatus());
things.add(s);
});
jsonGenerator.writeObject(things);
}
}
Thing Entity
#Entity
#Table(name = "thing")
public class Thing extends BaseEntity implements Persistence {
private static final long serialVersionUID = 721739537425505668L;
private String createdBy;
private Date creationDate;
.
.
.
#OneToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "ThingOwner", joinColumns = #JoinColumn(name = "thing_id") , inverseJoinColumns = #JoinColumn(name = "owner_id") )
private Set<Owner> owners = new HashSet<>();
Why don't you use ObjectMapper to serialize and deserialize your data?
Have a look at this small test, i believe it does what you want:
#Test
public void myTest() throws Exception{
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(
mapper.getSerializationConfig().
getDefaultVisibilityChecker().
withFieldVisibility(JsonAutoDetect.Visibility.ANY).
withGetterVisibility(JsonAutoDetect.Visibility.NONE).
withSetterVisibility(JsonAutoDetect.Visibility.NONE).
withCreatorVisibility(JsonAutoDetect.Visibility.NONE).
withIsGetterVisibility(JsonAutoDetect.Visibility.NONE));
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
mapper.setSerializationInclusion(Include.NON_NULL);
TestObject value = new TestObject();
value.a = "TestAvalue";
value.set = new HashSet<>();
value.set.add(new SetValueObject(1, 1));
value.set.add(new SetValueObject(2, 2));
String test = mapper.writeValueAsString(value);
System.out.println(test);
}
public class TestObject{
String a;
Set<SetValueObject> set;
}
public class SetValueObject{
public SetValueObject(int a, int b){
this.a = a;
this.b = b;
}
int a;
int b;
}
Output:
{"a":TestAvalue","set":[{"a":1,"b":1},{"a":2,"b":2}]}
Tested with Jackson 2.6.1
And i have modified one of my tests, so i'am not sure that you need all of this ObjectMapper config => it's just to give you an idea of another approach by using ObjectMapper.

Jackson: Object Mapper annotations to deserialize a inner collection

I want to convert the following json into a java object, using as much as possible annotations.
{"user":{
"id":1,
"diets":[
{"diet":{
"name":"...",
"meals":[]
}
}
]
}
}
I'm getting trouble with the collection diets. I tried to use #JsonProperty but it doesn't work properly. Is there a special annotation for map inner aggregates?
Diet.java
#JsonRootName(value = "diet")
public class Diet {
#JsonProperty(value="name")
private String name;
#JsonProperty(value="meals")
private List<Meal> meals;
private User user;
// Rest of the class omitted.
}
User.java
#JsonRootName(value = "user")
public class User {
#JsonProperty("id")
private long id;
#JsonProperty("diets")
private List<Diet> diets = new ArrayList<Diet>();
// Rest of the class omitted.
}
Thanks!
The diets object in your json is not a List. Its a List of key-value pair with key "diet" and value a diet object. So you have three options here.
One is to create a wrapper object say DietWrapper and use List of diet wrapper in User like
#JsonRootName(value = "user")
class User {
#JsonProperty(value = "id")
private long id;
#JsonProperty(value = "diets")
private List<DietWrapper> diets;
//Getter & Setters
}
class DietWrapper {
#JsonProperty(value = "diet")
Diet diet;
}
Second option is to keep diest as simple list of maps like List>
#JsonRootName(value = "user")
class User {
#JsonProperty(value = "id")
private long id;
#JsonProperty(value = "diets")
private List<Map<String, Diet>> diets;
//Getter & Setters
}
Third option is to use a custom deserializer which would ignore your diet class.
#JsonRootName(value = "user")
class User {
#JsonProperty(value = "id")
private long id;
#JsonProperty(value = "diets")
#JsonDeserialize(using = DietDeserializer.class)
private List<Diet> diets;
//Getter & Setters
}
class DietDeserializer extends JsonDeserializer<List<Diet>> {
#Override
public List<Diet> deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonParser);
List<Diet> diets = mapper.convertValue(node.findValues("diet"), new TypeReference<List<Diet>>() {});
return diets;
}
}

Jackson - Deserialize nested JSON

I have a JSON string which will be of the following format:
{
"response": {
"execution_status": "ready",
"report": {
"cache_hit": true,
"created_on": "2013-07-29 08:42:42",
"fact_cache_error": null,
"fact_cache_hit": true,
"header_info": null,
"name": null,
"report_size": "5871",
"row_count": "33",
"url": "report-download?id=278641c223bc4e4d63df9e83d8fcb4e6"
},
"status": "OK"
}
}
The response part of the JSON is common for a bunch of response types. The report part of this JSON holds good only for this response. So I had created a Response class as shown below with getters and setters (have not included the getters and setters here for brevity):
#JsonRootName(value = "response")
public class Response implements Serializable {
private static final long serialVersionUID = -2597493920293381637L;
#JsonProperty(value = "error")
private String error;
#JsonProperty(value = "error_code")
private String errorCode;
#JsonProperty(value = "error_id")
private String errorId;
#JsonProperty(value = "error_description")
private String errorDescription;
#JsonProperty(value = "method")
private String method;
#JsonProperty(value = "service")
private String service;
#JsonProperty(value = "status")
private String status;
#JsonProperty(value = "execution_status")
private String executionStatus;
}
And then, I created a Report class with the fields in the report element as below. The ReportResponse class will extend from the Response class (again the getters and setters are not included for brevity):
public class ReportResponse extends Response {
private static final long serialVersionUID = 4950819240030644407L;
#JsonProperty(value = "cache_hit")
private Boolean cacheHit;
#JsonProperty(value = "created_on")
private Timestamp createdOn;
#JsonProperty(value = "fact_cache_error")
private String factCacheError;
#JsonProperty(value = "fact_cache_hit")
private Boolean factCacheHit;
#JsonProperty(value = "header_info")
private String headerInfo;
#JsonProperty(value = "json_request")
private String jsonRequest;
#JsonProperty(value = "name")
private String name;
#JsonProperty(value = "report_size")
private Integer reportSize;
#JsonProperty(value = "row_count")
private Integer rowCount;
#JsonProperty(value = "url")
private String url;
}
Now when I use the ObjectMapper to map to the ReportResponse object, I get the following error:
String jsonString = "{\"response\": {\"execution_status\": \"ready\", \"report\": {\"cache_hit\": true, \"created_on\": \"2013-07-29 09:53:44\", \"fact_cache_error\": null, \"fact_cache_hit\": false, \"header_info\": null, \"name\": null, \"report_size\": \"5871\", \"row_count\": \"33\", \"url\": \"report-download?id=2ff62c07fc3653b68f2073e7c1aa0517\"}, \"status\": \"OK\"}}";
ObjectMapper mapper = new ObjectMapper();
ReportResponse reportResponse = mapper.readValue(jsonString, ReportResponse.class);
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "report"
I know that I can create a separate Report class and then embed it in the ReportResponse with the #JsonProperty anotation. Is there a way I can avoid that and mark the ReportResponse class with an annotation which would map it to the "report" element in the JSON?
There is no annotation which could handle this case yet. There is a ticket requesting this feature.
Here is a brief statement from one of the owners regarding this topic.
Quote from the mentioned statement:
Tatu Saloranta: "… #JsonProperty does not support transformations, since the data binding is based on incremental parsing and does not have access to full tree representation. Supporting #JsonUnwrapped was non-trivial, but doable; and thus converse ("#JsonWrapped") would be doable, theoretically speaking. Just plenty of work. …"
I see couple of problems in your code. First thing is that you don't have report attribute in your Response class, which is required as per the json structure you have shown. Secondly you need to provide the getters and setters in your bean classes as those will be used by the jackson for marhsalling and unmarshalling of json/object.

Serialize JSON Subclasses Information in Java

I need some help with one JSON serialization Problem.
I have one class like this:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorFormula("type")
#JsonSubTypes({ #JsonSubTypes.Type(value =TextFileCardEntity.class, name = "textCard"),
#JsonSubTypes.Type(value = MultipleChoiceFileCard.class, name = "choiceCard")#JsonSubTypes.Type(value = MultipleChoiceFileCard.class, name = "choiceCard")})
public class FileCardEntity extends ResourceEntity implements Cloneable {
#NotNull
#JsonIgnore
private FileCardType type;
private Date expirationDate;
.....
I also have two classes that extends of FileCardEntity:
#JsonTypeName(value = "textCard")
public class TextFileCardEntity extends FileCardEntity {
#Lob
#Type(type = "org.hibernate.type.TextType")
private String question;
#Lob
#Type(type = "org.hibernate.type.TextType")
private String answer;
.......
AND
#Entity
#DiscriminatorValue("1")
#JsonTypeName(value = "choiceCard")
public class MultipleChoiceFileCard extends FileCardEntity {
#Lob
#Type(type = "org.hibernate.type.TextType")
private String question;
#Lob
#Type(type = "org.hibernate.type.TextType")
private String crib;
......
I use the next Code in order to deserialize the information:
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
final ObjectWriter typedWriter = mapper.writerWithType(mapper.getTypeFactory().constructCollectionType(List.class, classElement));
System.out.println(typedWriter.writeValueAsString(elements));
Where elements is: List<?> elements
When I deserialize information from Server about TextFileCardEntity, I get:
[{"textCard":{"status":"ACTIVE","lastModified":1366293513200,"expirationDate":0.....
That is ok
When I attempt serialize information about TextFileCardEntity for example to Server, the code sends:
[{"FileCardEntity":{"status":"ACTIVE","lastModified":1366378327069,"shared":false,"orderID":0........
That is not ok because I need the syntax to be similar has in the deserialization information
--> [{"textCard":{"status".... and not [{"FileCardEntity":{"status"....
What can I do?

Categories