Apache Camel - GSON JsonSerializer use on routes - java

I have a endpoint with Camel that returns properties as JSON but are not with the proper order. The return class has a superclass that returns some control data which is necessarily to be present in every return.
public class Respuesta implements Serializable {
#SerializedName("subject")
#Expose
private String subject;
#SerializedName("action")
#Expose
private String action;
#SerializedName("status")
#Expose
private Integer status;
#SerializedName("description")
#Expose
private String description;
...getter/setter
And the final return class inherits that piece.
public class FacturadoresListarResponse extends Respuesta implements Serializable {
#SerializedName("lst")
#Expose
private List<Facturador> listaProveedores;
public FacturadoresListarResponse(List<Facturador> listaProveedores) {
super();
this.listaProveedores = listaProveedores;
}
public FacturadoresListarResponse() {
}
public void setRespuesta(Respuesta rsp) {
super.setAction(rsp.getAction());
super.setDescription(rsp.getDescription());
super.setStatus(rsp.getStatus());
super.setSubject(rsp.getSubject());
}
getter/setter...
}
So, the Gson's Marshaller takes first the inherited class property (lst), and then the parent class properties (subject, status, etc.), giving this kind of result on the wire.
{
"lst": [
{
"rut": "XXXX-X",
"rzsoc": "XXXXXXx",
"res": 1,
"ema": "a#a.cl"
}
],
"subject": "facturadores",
"action": "listar",
"status": 0,
"description": "OK"
}
I wrote a GSON custom JsonSerializer that builds data in order, but I can't use in a Camel DSL syntax. I tried, but without results:
.marshal().json(JsonLibrary.Gson,FacturadoresListarRspSerializer.class, true)
.convertBodyTo(String.class, "UTF-8")
Is there supported by Camel to use these kind of serializers to achieve proper order without migrating to Jackson?
Note: The code of the serializer (FacturadoresListarRspSerializer.class).
public class FacturadoresListarRspSerializer implements JsonSerializer<FacturadoresListarResponse> {
#Override
public JsonElement serialize(FacturadoresListarResponse src, Type typeOfSrc, JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("subject", src.getSubject());
jsonObject.addProperty("action", src.getAction());
jsonObject.addProperty("status", src.getStatus());
jsonObject.addProperty("description", src.getDescription());
final JsonArray jsarrFacturadores = new JsonArray();
for (final Facturador fact : src.getListaProveedores()) {
JsonObject jsobFacturadores = new JsonObject();
jsobFacturadores.addProperty("rut", fact.getRutCompleto());
jsobFacturadores.addProperty("rzsoc", fact.getRazonSocial());
jsobFacturadores.addProperty("res", fact.getResolucion());
jsobFacturadores.addProperty("ema", fact.getCorreoEnvio());
jsarrFacturadores.add(jsobFacturadores);
}
jsonObject.add("lst", jsarrFacturadores);
return jsonObject;
}
}

Create a new GSON instance:
Gson gson = new GsonBuilder().registerTypeAdapter(FacturadoresListarResponse.class,
new FacturadoresListarRspSerializer()).create();
Create a new GsonDataFormat by specifying the previously created Gson instance:
GsonDataFormat gsonDataFormat = new GsonDataFormat(gson, FacturadoresListarResponse.class);
Specify the previous data format in your RouteBuilder's marshal(DataFormat dataFormat) method:
.marshal(gsonDataFormat)

Related

How to write a TypeAdapter for multiple JSON objects with Gson?

I have a JSON feed with multiple nested JSON objects. I have written my POJO classes and have looked on here for how to deserialize nested JSON objects to access the data I needed. However I am still receiving NullPointerExceptions on my nested JSON objects:
JSON feed example
{
"data": [
{
"relationships": {
"dismissals": {
"meta": {
"count": {
"home": 0,
"away": 0
}
},
"data": []
},
"home": {
"data": {
"type": "teams",
"id": "2"
}
}
}
}
]
}
Pojo Mappings
Relationships:
public class Relationships implements Serializable
#SerializedName("region")
#Expose
private Region region;
#SerializedName("competition")
#Expose
private Competition competition;
getters and setters
}
Region:
public class Region implements Serializable
{
#SerializedName("data")
#Expose
private Data data;
}
Data
public class Data implements Serializable, Parcelable
{
#SerializedName("type")
#Expose
private String type;
#SerializedName("id")
#Expose
private String id;
}
My TypeAdapter
public class ItemTypeDataFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}}
Retrofit builder:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new ItemTypeDataFactory()) // This is the important line ;)
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RequestInterface request = retrofit.create(RequestInterface.class);
For example I want to get:
getRelationships().getDissmissals().Meta().Count().Home();
When I run my app I get NullPointerException for that.
Is there something I need to add to my type adapter to deserialize the classes so I can get the data in multiple nested JSON objects? I have tried looking on here already and nothing has helped.
Some times it's difficult to generate the POJO class for a JSON response.
You can generate your POJO easily here http://www.jsonschema2pojo.org/

How to parse Json array of different objects with Gson?

Json string:
[
//Object 1
{
TypeName:"CheckSpecificDday",
SpecificDay:"20160413",
Lunar:1
},
{
TypeName:"CheckSpecificDday",
SpecificDay:"20160414",
Lunar:1
},
//Object 2
{
TypeName:"CheckEveryDayDday",
StartDate:"20160413",
EndDate:"20260417",
Interval:1,
StartOption:"D",
HolidayCondition:1
},
//Object 3
{
TypeName:"CheckEveryDdayOfWeek",
StartDate:"20160413",
EndDate:"",
Interval:1,
SpecificDayOfWeek:"3",
HolidayCondition:1
},
//Object 4
{
TypeName:"CheckEveryMonthSpecificDday",
StartDate:"20160413",
EndDate:"",
Interval:1,
SpecificDD:"13,14",
HolidayCondition:1
},
//Object 5
{
TypeName:"CheckEveryYearWeek",
StartDate:"20160413",
EndDate:"",
Interval:1,
SpecificMMnthWeek:"0433",
HolidayCondition:1
}
]
I have a Json array like the above. What I want is to parse it to different object types with Gson (as I commented to make it clearer), but I dont know how to do that. Please help me. Thank you in advance!
I think there are lots of simmilar questions on SO. One, Two
One way to parse this is to use simple
Object[] result = new Gson().fromJson(json, Object[].class);
But this will give you objects of LinkedTreeMap<Integer, LinkedTreeMap<String, String>> or something like this. You can use it, but its kinda hard and you will also have problems with your integers comming as doubles.
The other approach is to create custom interface or abstract class with TypeName field if you need it:
private interface CheckInterface{}
and implement it with every POJO classes of object types you have:
private static class CheckEveryDayBase implements CheckInterface{
private String StartDate;
private String EndDate;
private int Interval;
private int HolidayCondition;
}
private static class CheckSpecificDday implements CheckInterface{
private String SpecificDay;
private int Lunar;
}
private static class CheckEveryDayDday extends CheckEveryDayBase{
private String StartOption;
}
private static class CheckEveryDdayOfWeek extends CheckEveryDayBase{
private String SpecificDayOfWeek;
}
private static class CheckEveryMonthSpecificDday extends CheckEveryDayBase{
private String SpecificDD;
}
private static class CheckEveryYearWeek extends CheckEveryDayBase{
private String SpecificMMnthWeek;
}
Then create custom desrializer for your CheckInterface:
public static class CheckInterfaceDeserializer implements JsonDeserializer<CheckInterface>{
#Override
public CheckInterface deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonObject jObject = (JsonObject) json;
JsonElement typeObj = jObject.get("TypeName");
if(typeObj!= null ){
String typeVal = typeObj.getAsString();
switch (typeVal){
case "CheckSpecificDday":
return context.deserialize(json, CheckSpecificDday.class);
case "CheckEveryDayDday":
return context.deserialize(json, CheckEveryDayDday.class);
case "CheckEveryDdayOfWeek":
return context.deserialize(json, CheckEveryDdayOfWeek.class);
case "CheckEveryMonthSpecificDday":
return context.deserialize(json, CheckEveryMonthSpecificDday.class);
case "CheckEveryYearWeek":
return context.deserialize(json, CheckEveryYearWeek.class);
}
}
return null;
}
}
Here is how you can use this:
GsonBuilder builder = new GsonBuilder();
// Register custom deserializer for CheckInterface.class
builder.registerTypeAdapter(CheckInterface.class, new CheckInterfaceDeserializer());
Gson gson = builder.create();
CheckInterface[] result2 = gson.fromJson(json, CheckInterface[].class);

Json Serialize and Deserialize complex content using Gson

I have a structure that I want to store using JSON in a file. None of the implementation classes will have more significant information than what is given.
public class ItemExample implements IItem{
private ModelMap map;
private String name;
}
public class ModelMap {
private HashMap<Coord, IPartType> map;
}
public class Coord {
private int x,y,z;
}
public class PartExample implements IPartType {
private String name;
private Purity purity;
}
public Enum Purity{
}
I am brand new to creating JSONs, I've been reading up on how Gson works but I am not really understanding how to translate the examples to my case. Most examples assume knowledge of certain aspects that I just don't know yet.
This is what I have started to do for IPartType:
public class PartDeserialize<T> implements JsonDeserializer<T>{
#Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject content = json.getAsJsonObject();
String name = content.get("name").getAsString();
Purity purity = Purity.valueOf(content.get("purity").getAsString());
return new Gson().fromJson(content, typeOfT);
}
}
I would appreciate any help.
Possible JSON Example per request:
{
"name" : "sword",
"map" :
{
"map" :
{
"1,1,1" : //string representation of Coord
{
"name" : "blade",
"purity" : "base" // string representation of Purity Enum
},
"0,0,0" :
{
"name" : "handle",
"purity" : "high"
}
}
}
}

serialize json array into java objects

I have a JSON array like as shown below which I need to serialize it to my class. I am using Jackson in my project.
[
{
"clientId": "111",
"clientName": "mask",
"clientKey": "abc1",
"clientValue": {}
},
{
"clientId": "111",
"clientName": "mask",
"clientKey": "abc2",
"clientValue": {}
}
]
In above JSON array, clientValue will have another JSON object in it. How can I serialize my above JSON array into my java class using Jackson?
public class DataRequest {
#JsonProperty("clientId")
private String clientId;
#JsonProperty("clientName")
private int clientName;
#JsonProperty("clientKey")
private String clientKey;
#JsonProperty("clientValue")
private Map<String, Object> clientValue;
//getters and setters
}
I have not used jackson before so I am not sure how can I use it to serialize my JSON array into Java objects? I am using jackson annotation here to serialize stuff but not sure what will be my next step?
You can create a utility function shown below. You may want to change the Deserialization feature based on your business needs. In my case, I did not want to fail on unknown properties => (FAIL_ON_UNKNOWN_PROPERTIES, false)
static <T> T mapJson(String body,
com.fasterxml.jackson.core.type.TypeReference<T> reference) {
T model = null;
if(body == null) {
return model;
}
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
try {
model = mapper.readValue(body, reference);
} catch (IOException e) {
//TODO: log error and handle accordingly
}
return model;
}
You can call it using similar approach as shown below:
mapJson(clientValueJsonString,
new com.fasterxml.jackson.core.type.TypeReference<List<DataRequest>>(){});
You can try #JsonAnyGetter and #JsonAnySetter annotations with an inner class object. Also clientName should have type String, not int.
public class DataRequest {
private String clientId;
private String clientName;
private String clientKey;
private ClientValue clientValue;
//getters and setters
}
public class ClientValue {
private Map<String, String> properties;
#JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
#JsonAnyGetter
public Map<String,String> getProperties() {
return properties;
}
}

Gson parse to POJO with custom key

I have json data like this:
meds:
[
{
MedsID: 8063,
updated_at: "2015-11-04T06:59:55",
treatment_date: "2015-11-04T00:00:00",
name: "name"
}
],
scores:
[
{
ScoreID: 75820,
updated_at: "2015-11-04T06:59:55"
dialysis_flow: 233,
}
],
dias:
[
{
DiasID: 75820,
updated_at: "2015-11-04T06:59:55"
name: "K",
}
]
And here is my Entities:
public class BaseData{
public long id;
}
public class Med extends BaseData{
public String name;
public String updated_at;
public String treatment_date;
}
public class Score extends BaseData{
public String updated_at;
public int dialysis_flow;
}
public class Dias extends BaseData{
public String name;
public String updated_at;
public String treatment_date;
}
Because all entities are mapped from database with the id key (as I use orm db, it's loaded by property name ). So I need to parse all other keys MedsID, DiasID, ScoreID into id when mapping by gson. Is there any way to achieve that?
Update:
I use registerTypeHierarchyAdapter instead of registerTypeAdapter and it can work. But this way is extremely slow as my json data is very large.
public class DataDeserializer implements JsonDeserializer<BaseData> {
#Override
public BaseData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject ja = json.getAsJsonObject();
Gson gson = new Gson();
final String[] mapIds = {"ScoreID", "MedsID", "DiasID"};
BaseData data = gson.fromJson(ja, typeOfT);
for (String idKey:mapIds){
if(ja.has(idKey)){
data.id = ja.get(idKey).getAsLong();
break;
}
}
return data;
}
}
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter( BaseData.class, new DataDeserializer() ).create();
Does anyone know other way to achieve that?
The only way to achieve this is writing a custom de-serializer. Please see below example:
public class CustomDeserializer implements JsonDeserializer<Dias>{
public Dias deserialize( JsonElement json, Type typeOfT, JsonDeserializationContext context ) throws JsonParseException{
JsonObject ja = json.getAsJsonObject();
Dias dias = new Gson().fromJson( ja, Dias.class );
dias.id = ja.get( "DiasID" ).getAsLong();
return dias;
}
}
Register it;
String dias = "{'DiasID':75820,'updated_at':'2015-11-04T06:59:55','name':'K'}";
Gson gson = new GsonBuilder().registerTypeAdapter( Dias.class, new CustomDeserializer() ).create();
Dias dias2 = gson.fromJson( dias, Dias.class );
System.out.println( dias2.id );
Output:
75820
This is just an example, you can extend it by writing a deserializer for your own base class.

Categories

Resources