I'm trying to create a java model for my json response in a melanoma detection app.
My response looks like this:
{
"success": true,
"predictions": [
{
"label": "Non-melanoma",
"probability": 0.016881238669157028
},
{
"label": "Melanoma",
"probability": 0.9831187129020691
}
]
}
I usually go with https://www.jsonschema2pojo.org/ in creating my java model from json, but this time I am getting this:
-----------------------------------com.example.Example.java-----------------------------------
package com.example;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
#Generated("jsonschema2pojo")
public class Example {
#SerializedName("success")
#Expose
private Boolean success;
#SerializedName("predictions")
#Expose
private List<Prediction> predictions = null;
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public List<Prediction> getPredictions() {
return predictions;
}
public void setPredictions(List<Prediction> predictions) {
this.predictions = predictions;
}
}
-----------------------------------com.example.Prediction.java-----------------------------------
package com.example;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
#Generated("jsonschema2pojo")
public class Prediction {
#SerializedName("label")
#Expose
private String label;
#SerializedName("probability")
#Expose
private Double probability;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Double getProbability() {
return probability;
}
public void setProbability(Double probability) {
this.probability = probability;
}
}
which leads to different files that I don't know how to use later.
I'd like to have one response model, like response_model.java to use like this in the app:
Call<response_model> call = getResponse.uploadFile(fileToUpload, filename);
call.enqueue((Callback<response_model>)(new Callback<response_model>() {
public void onResponse(#NotNull Call call, #NotNull Response response) {
Intrinsics.checkParameterIsNotNull(call, "call");
Intrinsics.checkParameterIsNotNull(response, "response");
if (response.isSuccessful()) {
Log.v("upload", "response succ");
response_model serverResponse = (response_model) response.body();
if (serverResponse.getPredictions()!=null) {
((TextView)findViewById(R.id.output_text)).setText(serverResponse.getPredictions().toString());
} else {
loader.setVisibility(View.GONE);
Toast.makeText(getApplicationContext(), "response null",Toast.LENGTH_SHORT).show();
}
} else {
Log.v("Response 1", "wasnt successfull");
}
}
Is there a way?
Actually your java model is working just fine.
You can access each label/probability as a list element, by:
serverResponse.getPredictions().get(0).getLabel()
serverResponse.getPredictions().get(0).getProbability()
(which should give you the 1st Label-Probability element pair).
If you're always gonna have 2 elements in your response Prediction list (one for melanoma and one for non-melanoma) you can easily hard-code it with get(0) and get(1).
You can use this link to generate pojo
https://json2csharp.com/json-to-pojo
This is how your pojo will looks like in a single file
package com.test.test;
import java.util.List;
class Prediction{
public String label;
public double probability;
}
public class Test{
public boolean success;
public List<Prediction> predictions;
}
Related
I'm using Spring 2.6 and we make a GET request via
restTemplate.exchange(url, HttpMethod.GET, httpEntity, ResponseType.class).getBody();
The JSON response can be of two kinds:
1st:
public class ResponseType {
private String data;
}
2nd:
public class ResponseType {
private Subclass data;
}
public class Subclass {
private String classId;
private String detail;
}
In the first version I only get a reference link to the subclass resource.
If the URL contains a 'resolve' flag, than the reference link get expanded already in the first request.
The classId then also specifies what kind of class it is ( 'a.b.c' or 'x.y.z' )
No problem for JSON, but how can I get a mapping in Java?
When having more fields being dynamic (link or instance based on classId) a manual way would be difficult to implement if the combination could be 2 links and 3 objects.
It also could be that a object has the same feature - a filed with a link or a instance of a class specified by classId.
The JSON response would be this:
{
"data": "abskasdkjhkjsahfkajdf-linkToResource"
}
or this:
{
"data": {
"classId": "a.b.subclass",
"detail": "some data"
}
}
or this:
{
"data": {
"classId": "a.b.subclass",
"detail": "some data"
"data2": "some-link-id",
"data3": {
"detailB": "foo",
"detailC": "some-link-id"
}
}
}
Here I do have a possible solution for my problem. The logic to print the address only or the POJO relies soley in the CustomItemSerializer. So it is possible to use this without using duplicate code in controllers.
package com.allianz.clana.datamodel.http.epc.test;
import java.io.IOException;
import java.text.ParseException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import lombok.Data;
import lombok.NoArgsConstructor;
public class JacksonTester2 {
public static void main(String args[]) throws ParseException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Item item2 = new Item("link");
Stuff stuff = new Stuff();
stuff.setItem(item2);
stuff.setFoo("foo");
String jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
System.out.println(jsonStringStuff);
Item item3 = new Item("{ \"name\":\"ID3\", \"creationDate\":\"1984-12-30\", \"rollNo\": 1 }");
stuff.setItem(item3);
stuff.setFoo("bar");
jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
System.out.println(jsonStringStuff);
}
}
class CustomItemSerializer extends StdSerializer<Item> {
private static final long serialVersionUID = 1L;
public CustomItemSerializer() {
this(null);
}
public CustomItemSerializer(Class<Item> t) {
super(t);
}
#Override
public void serialize(Item item, JsonGenerator generator, SerializerProvider arg2) throws IOException {
if (item != null) {
if (item.getItem() != null) {
System.out.println("ItemA POJO data");
generator.writePOJO(item.getItem());
} else {
System.out.println("raw data with link");
generator.writeString(item.getRawdata());
}
}
}
}
#Data
class Stuff {
Item item;
String foo;
}
#JsonSerialize(using = CustomItemSerializer.class)
#Data
#NoArgsConstructor
class Item {
private String rawdata;
#JsonIgnore
private ItemA item;
public Item(String rawdata) {
this.rawdata = rawdata;
if (rawdata.contains("{")) {
try {
this.item = new ObjectMapper().readerFor(ItemA.class).readValue(rawdata);
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
}
#Data
#NoArgsConstructor
class ItemA{
private String name;
private int rollNo;
private String creationDate;
public ItemA(String name, int rollNo, String dob) {
this.name = name;
this.rollNo = rollNo;
this.creationDate = dob;
}
}
The output looks like this:
raw data with link
{
"item" : "link",
"foo" : "foo"
}
ItemA POJO data
{
"item" : {
"name" : "ID3",
"rollNo" : 1,
"creationDate" : "1984-12-30"
},
"foo" : "bar"
}
The CustomItemSerializer decides if the link is printed or the POJO.
I have REST api, and when client call POST request with body, backend after deserialize should distinguish null from the absence of a value.
Because if value in JSON is null, then value in DB should become null.
If value in JSON absence, then value in DB should remain unchanged.
JSON:
{
"id" : 1,
"name" : "sample name",
"value" : null
}
OR
{
"id" : 1,
"name" : "sample name"
}
For Java after deserialization it is look like : value = null;
Java:
#Entity
#Table("sample")
public class Sample {
#Id
#Column
private Long id;
#Column
private String name;
#Column
private Integer value;
// getters / setters
}
Sample REST request:
#PutMapping
public ResponseEntity<SampleDto> updateSample(#RequestBody SampleDto dto) {
return ResponseEntity.ok(service.updateSample(dto));
}
Sample service impl:
public SampleDto updateSample(SampleDto dto) {
Sample sample = sampleRepository.findById(dto.getId);
sample.setName(dto.getName());
sample.setValue(dto.getValue());
//In this operation back need understand: value is null or absence
//Because if value in JSON is null, then value in DB should become null
//If value in JSON absence, then value in DB should remain unchanged
Sample newSample = sampleRepository.save(sample);
return modelMapper.map(newSample, SampleDto.class);
}
Project use Spring Data.
Maybe I should use #JsonDeserialize annotation or other Hibernate annotation
I tried use #JsonDeserialize, but it is not solution.
Partial update is different from full-resource update and we should implement it in a different way. Let's create two request POJO classes. One class will be used to create and update resources, second will be used to partially update given resource. To emphasise it we will use different HTTP methods. To distinguish null from absence we can use java.util.Optional class.
SampleCompleteRequest class we use together with POST (create) and PUT (update) methods.
SamplePartialRequest class we use together with PATCH (partially update) method.
To avoid boilerplate code in this example I'm using Lombok and MapStruct but it is not required.
Model
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
#Data
public class SampleCompleteRequest {
#NotBlank
private String name;
private String value;
}
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.Optional;
#Data
public class SamplePartialRequest {
private Optional<#NotBlank String> name;
private Optional<String> value;
}
import lombok.Data;
#Data
public class SampleResponse {
private Long id;
private String name;
private String value;
}
import lombok.Data;
#Data
public class Sample {
//#Id - Hibernate annotations are removed
private Long id;
private String name;
private String value;
}
MapStruct
In MapStruct we need to define an interface with all methods we need.
import com.example.demo.model.SampleCompleteRequest;
import com.example.demo.model.SamplePartialRequest;
import com.example.demo.model.SampleResponse;
import jakarta.annotation.Nullable;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.ReportingPolicy;
import java.util.Optional;
import static org.mapstruct.MappingConstants.ComponentModel.SPRING;
import static org.mapstruct.NullValueCheckStrategy.ALWAYS;
import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = SPRING)
public interface SamplesMapper {
#BeanMapping(nullValueCheckStrategy = ALWAYS, nullValuePropertyMappingStrategy = IGNORE)
Sample patch(SamplePartialRequest input, #MappingTarget Sample target);
Sample update(SampleCompleteRequest input, #MappingTarget Sample target);
SampleResponse mapToResponse(Sample input);
default String optionalToString(#Nullable Optional<String> nullable) {
return nullable == null ? null : nullable.orElse(null);
}
}
Plugin will generate boilerplate code for us. Below class is autogenerated and we do not need to implement it manually.
#Component
public class SamplesMapperImpl implements SamplesMapper {
#Override
public Sample patch(SamplePartialRequest input, Sample target) {
if ( input == null ) {
return target;
}
if ( input.getName() != null ) {
target.setName( optionalToString( input.getName() ) );
}
if ( input.getValue() != null ) {
target.setValue( optionalToString( input.getValue() ) );
}
return target;
}
#Override
public Sample update(SampleCompleteRequest input, Sample target) {
if ( input == null ) {
return target;
}
target.setName( input.getName() );
target.setValue( input.getValue() );
return target;
}
#Override
public SampleResponse mapToResponse(Sample input) {
if ( input == null ) {
return null;
}
SampleResponse sampleResponse = new SampleResponse();
sampleResponse.setId( input.getId() );
sampleResponse.setName( input.getName() );
sampleResponse.setValue( input.getValue() );
return sampleResponse;
}
}
Resource
A controller class is easy to implement:
import com.example.demo.model.SampleCompleteRequest;
import com.example.demo.model.SamplePartialRequest;
import com.example.demo.model.SampleResponse;
import com.example.service.SamplesMapper;
import com.example.service.SamplesService;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#AllArgsConstructor
#RestController
#RequestMapping(value = "/api/v1/samples")
public class SamplesResource {
private final SamplesMapper mapper;
private final SamplesService samplesService;
#GetMapping
public CollectionModel<SampleResponse> listAll() {
List<SampleResponse> entities = samplesService.list().stream().map(mapper::mapToResponse).toList();
return CollectionModel.of(entities);
}
#PostMapping
public EntityModel<SampleResponse> addSample(#Valid #RequestBody SampleCompleteRequest request) {
var entity = samplesService.create(request);
var response = mapper.mapToResponse(entity);
return EntityModel.of(response);
}
#PutMapping(path = "{id}")
public EntityModel<SampleResponse> updateSample(#PathVariable Long id, #Valid #RequestBody SampleCompleteRequest request) {
var entity = samplesService.update(id, request);
var response = mapper.mapToResponse(entity);
return EntityModel.of(response);
}
#PatchMapping(path = "{id}")
public EntityModel<SampleResponse> partiallyUpdateSample(#PathVariable Long id, #Valid #RequestBody SamplePartialRequest request) {
var entity = samplesService.patch(id, request);
var response = mapper.mapToResponse(entity);
return EntityModel.of(response);
}
}
A service class is also straightforward:
import com.example.demo.model.SampleCompleteRequest;
import com.example.demo.model.SamplePartialRequest;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
#AllArgsConstructor
public class SamplesService {
private final SamplesMapper mapper;
private final SamplesRepository repository;
public List<Sample> list() {
return repository.listAll();
}
public Sample create(SampleCompleteRequest request) {
var sample = mapper.update(request, new Sample());
return repository.save(sample);
}
public Sample update(Long id, SampleCompleteRequest request) {
var sample = repository.find(id).orElseThrow();
mapper.update(request, sample);
return repository.save(sample);
}
public Sample patch(Long id, SamplePartialRequest request) {
var sample = repository.find(id).orElseThrow();
mapper.patch(request, sample);
return repository.save(sample);
}
}
See also:
HTTP PUT vs HTTP PATCH in a REST API
Difference between Jackson objectMapper to others
Spring MVC PATCH method: partial updates
I'm having an issue - I don't know why the body returns null here is my model.
package com.example.currencyapp.model;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
public class Rates implements Serializable {
#SerializedName("CAD")
#Expose
private String cad;
public Rates(String cad) {
this.cad = cad;
}
public Rates() {
}
public String getCad() {
return cad;
}
}
and here is my json
{
"rates": {
"CAD": 1.5399,
}
}
and this is my service
import com.example.currencyapp.model.Rates;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface GetCurrencyDataService {
#GET("/latest")
Call<Rates> getCurrencyData();
}
and my retrofit instance
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitInstance {
private static Retrofit retrofit;
private static final String BASE_URL = "https://api.exchangeratesapi.io";
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
JSON object you presented expects your model to be:
public class CadObject implements Serializable {
#SerializedName("rates")
#Expose
private Rates rates;
...
class Rates implements Serializable {
#SerializedName("CAD")
#Expose
private String cad;
...
}
}
The reason for this is that you have a JSON object which holds a JSON object which holds a string value.
If you want your current model to work JSON object structure should look like this:
{
"CAD": 1.5399
}
I devellop an android application on android studio. I use java.
I want to use this api from open food facts : https://fr.openfoodfacts.org/api/v0/produit/3029330003533.json
But I only know how to use retrofit and Rxjava with only one pojo class.
I use this website to create pojo classe : http://pojo.sodhanalibrary.com
But he creates loads of pojo class and I don't know if it's correct and how i can use it ?
Next you can see that i have loads of POJO class.
POJO class
Use JsonSchema for generating pojo for the parsing library you are using(GSON/Jackson etc) and for Api calling user RxJava
and retrofit like this
Create Pojo
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.foodit.data.remote.wrapper.SignupDetailsWrapper;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"code",
"msg",
"details"
})
public class LoginResponse {
#JsonProperty("code")
private int code;
#JsonProperty("msg")
private String msg;
#JsonProperty("details")
private List<LoginDetailsWrapper> details = new ArrayList<LoginDetailsWrapper>();
#JsonProperty("code")
public int getCode() {
return code;
}
#JsonProperty("code")
public void setCode(int code) {
this.code = code;
}
#JsonProperty("msg")
public String getMsg() {
return msg;
}
#JsonProperty("msg")
public void setMsg(String msg) {
this.msg = msg;
}
#JsonProperty("details")
public List<LoginDetailsWrapper> getDetails() {
return details;
}
#JsonProperty("details")
public void setDetails(List<LoginDetailsWrapper> details) {
this.details = details;
}
}
Define Api in ApiInterface like this
#FormUrlEncoded
#POST("login")
Observable<LoginResponse> userLogin(#Field("device_id") String device_id, #Field("device_type") String device_type,
#Field("username") String username, #Field("password") String password
);
and Call api like this
#Override
public void userLogin(String device_id, String device_type, String username, String password) {
getCompositeDisposable().add(loginActivtiyInteractor.userLogin(device_id, device_type, username, password)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(loginResponse -> {
if (loginResponse != null) {
if (loginResponse.getCode() == 1) {
getMvpView().hideLoading();
getMvpView().updateView(loginResponse);
} else {
getMvpView().hideLoading();
getMvpView().onError(loginResponse.getMsg());
}
}
}, throwable -> {
throwable.printStackTrace();
getMvpView().onError(throwable.getLocalizedMessage());
getMvpView().hideLoading();
}));
}
I hope it helps.
While assigning values from DTO to different entity names, I assigned correctly with the help of JsonProperty. FrontEnd expecting in a different name. Values for DTO object will get from different object. That I have to assign to entity. Instead of using plain java and copying, am using objectmapper. Here then entity values will be used by frontend. How to print the entity values in different name? Please check below code.
//DTO Class
import java.util.List;
public class StaffDTO {
private String nameDT;
private List<String> skillDT;
//Getter and Setters
}
//Entity Class
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"nameDT",
"skillDT"
})
public class Staff {
#JsonProperty("nameDT")
private String name;
#JsonProperty("skillDT")
private List<String> skills;
//Getter and Setters
}
//Call Method
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SeatMapCall1 {
public static void main(String[] args) {
try {
StaffDTO staffDTO = createDummyObject();
System.out.println(convertObjectToJson(staffDTO));
Staff staff= convertJsonToObject(convertObjectToJson(staffDTO),Staff.class);
System.out.println(convertObjectToJson(staff));
} catch (Throwable e) {
e.printStackTrace();
}
}
private static <T> T convertJsonToObject(String jsonStrRes,
Class<T> classArg) {
T resObj = null;
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
resObj = mapper.readValue(jsonStrRes, classArg);
} catch (Throwable e) {
e.printStackTrace();
}
return resObj;
}
public static <T> String convertObjectToJson(T obj) {
String jsonStringReq = null;
ObjectMapper objMapper = new ObjectMapper();
try {
jsonStringReq = objMapper.writeValueAsString(obj);
} catch (Exception e) {
}
return jsonStringReq;
}
private static StaffDTO createDummyObject() {
StaffDTO staffDTO = new StaffDTO();
staffDTO.setNameDT("mkyong");
List<String> skills = new ArrayList<>();
skills.add("java");
skills.add("python");
staffDTO.setSkillDT(skills);
return staffDTO;
}
}
//Displays output as
{"nameDT":"mkyong","skillDT":["java","python"]}
{"nameDT":"mkyong","skillDT":["java","python"]}
But I want
{"nameDT":"mkyong","skillDT":["java","python"]}
{"name":"mkyong","skills":["java","python"]}
If I use the below getter and setters in Staff class, I am getting expected as below
{"nameDT":"mkyong","skillDT":["java","python"]}
{"nameDT":"mkyong","skillDT":["java","python"],"name":"mkyong","skills":["java","python"]}
Here it includes both nameDT, skillDT and name, skills. I don't need nameDT, skillDT.
#JsonProperty("nameDT")
private String nameDT;
#JsonProperty("skillDT")
private List<String> skillDT;
public String getName()
{ return nameDT; }
public void setName(String nameDT)
{ this.nameDT = nameDT; }
public List<String> getSkill()
{ return skillDT; }
public void setSkill(List<String> skillDT)
{ this.skillDT = skillDT; }