UpperCaseAdapter does not support json Array - java

I have created a UpperCaseAdapter to upperCase all the keys of a json ,
But not working as expected
Here is my UpperCaseAdapter code , i want to also upper case the arrays part but it is not the case for me , how could correct that ?
public static class UpperCaseAdapter implements JsonSerializer<Map<String, Object>>, JsonDeserializer<Map<String, Object>> {
public static final Type TYPE = new TypeToken<Map<String, Object>>() {}.getType();
#Override
public JsonElement serialize(Map<String, Object> src, Type typeOfSrc, JsonSerializationContext context) {
// TODO implement serialization if needed
return null;
}
#Override
public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Map<String, Object> map = new HashMap<>();
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
Object value = null;
if (entry.getValue().isJsonPrimitive()) {
value = entry.getValue().getAsString();
} else if (entry.getValue().isJsonObject()) {
value = context.deserialize(entry.getValue(), TYPE); // deserialize the object using the same type
} else if (entry.getValue().isJsonArray()) {
// TODO implement deserailization of array
} else if (entry.getValue().isJsonNull()) {
// skip nulls
continue;
}
map.put(entry.getKey().toUpperCase(), value); //toUpperCase() is what we want
}
return map;
}
}
and this code is to call my UpperCaseAdapter
String payload = input_row.body;
Gson gson = new GsonBuilder()
.registerTypeAdapter(json_routine.UpperCaseAdapter.TYPE, new
json_routine.UpperCaseAdapter())
.create();
Map<String, Object> mapDeserialized = gson.fromJson(payload,
json_routine.UpperCaseAdapter.TYPE);
System.out.println("**** body_WS" + gson.toJson(mapDeserialized));
My body looks like this :
{
"transactionId": 181,
"ExtWareHouseId ": "toto",
"OwnerCode": "toto",
"ClientCode": "toto",
"activityId": toto,
"taskTypeCode": "01",
"activated": "2023-02-06T17:33:48.1905172+01:00",
"StartDate ": "2023-01-24T15:19:12.8591383+01:00",
"userCode": "toto",
"equipmentId": "01",
"PortId": "1",
"printerName": "",
"shipments": [
{
"shipmentId": 2,
"picklistId": "Example",
"ExtOrderId": "23-127",
"boxType": "120"
}
]
}
I get the following result :
{
"EXTWAREHOUSEID ":"toto",
"PRINTERNAME":"",
"TRANSACTIONID":"181",
"OWNERCODE":"toto",
"ACTIVITYID":"1072",
"EQUIPMENTID":"01",
"USERCODE":"toto",
"STARTDATE ":"2023-01-24T15:19:12.8591383+01:00",
"ACTIVATED":"2023-02-06T17:33:48.1905172+01:00",
"TASKTYPECODE":"01",
"CLIENTCODE":"toto",
"PORTID":"1"
}
The shipments part is missing , how could i correct my adapter to get a correct result
Any help on this , i'm stack on how to Upper case Array keys

Related

How to check each item in a json object?

I have a json object nested as the example below and I want to check an item inside that object whether what it should be ===> Could you tell me how to do it?
E.g: I want to check whether the item contract_date is in valid format as yyyymmdd hh:mm:ss
{
"m_list" : [ {
"contract" : {
"category" : 1,
"cor_num" : 101,
"contract_name" : "ABC",
"contract_date" : "20230220 15:30:11"
},
"bu_unit" : {
"bu_name" : "1-1E"
}
} ]
}
I tried doing the following way but Katalon showed this error: No signature of method: static CommonFc.getAllKeys() is applicable for argument types: (java.util.LinkedHashMap, java.util.ArrayList
Possible solutions: getAllKeys(java.util.Map, java.util.List)
public List<String> getKeysInJsonUsingMaps(String json, ObjectMapper mapper) {
List<String> keys = new ArrayList<>();
Map<String, Object> jsonElements = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
});
getAllKeys(jsonElements, keys);
return keys;
}
public void getAllKeys(Map<String, Object> jsonElements, List<String> keys) {
jsonElements.entrySet()
.forEach({def entry ->
keys.add(entry.getKey());
if (entry.getValue() instanceof Map) {
Map<String, Object> map = (Map<String, Object>) entry.getValue();
getAllKeys(map, keys);
} else if (entry.getValue() instanceof List) {
List<?> list = (List<?>) entry.getValue();
list.forEach({ def listEntry ->
if (listEntry instanceof Map) {
Map<String, Object> map = (Map<String, Object>) listEntry;
getAllKeys(map, keys);
}
});
}
});
}

Convert nested JSON Keys to UPPERCASE

I want to convert all keys of JSON string/object to UPPERCASE in Java. The JSON can be nested.
I tried setting FieldNamingPolicy.UPPER_CAMEL_CASE in GsonBuilder but I guess that just works for String to JAVA Object and not for String to String.
String payload = "{\"key\" : {\"key1\" : \"value1\",\"key2\" : \"value2\"}}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapterFactory(myCustomTypeAdapterFactory);
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
Gson gson = gsonBuilder.create();
Map mapDeserialized = gson.fromJson(payload, Map.class);
System.out.println("Map " + mapDeserialized);
There are other solutions through JACKSON with custom TypeAdapterFactory but those only work for one level and not for nested.
{"key" : {
"key1" : "value1",
"key2" : "value2"
}}
to
{"KEY" : {
"KEY1" : "value1",
"KEY2" : "value2"
}}
As you said FieldNamingPolicy is applied only for bean fields not for map keys. However UPPER_CAMEL_CASE is not what you want, it is camel case with first letter capitalized (SometingLikeThis). You have to implement your own deserializer that would do that for your:
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class UpperCaseAdapter implements JsonSerializer<Map<String, Object>>, JsonDeserializer<Map<String, Object>> {
public static final Type TYPE = new TypeToken<Map<String, Object>>() {}.getType();
#Override
public JsonElement serialize(Map<String, Object> src, Type typeOfSrc, JsonSerializationContext context) {
// TODO implement serialization if needed
return null;
}
#Override
public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Map<String, Object> map = new HashMap<>();
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
Object value = null;
if (entry.getValue().isJsonPrimitive()) {
value = entry.getValue().getAsString();
} else if (entry.getValue().isJsonObject()) {
value = context.deserialize(entry.getValue(), TYPE); // deserialize the object using the same type
} else if (entry.getValue().isJsonArray()) {
// TODO implement deserailization of array
} else if (entry.getValue().isJsonNull()) {
// skip nulls
continue;
}
map.put(entry.getKey().toUpperCase(), value); //toUpperCase() is what we want
}
return map;
}
}
you can use the adapter then:
String payload = "{\"key\" : {\"key1\" : \"value1\",\"key2\" : \"value2\"}, \"key3\": \"value\"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(UpperCaseAdapter.TYPE, new UpperCaseAdapter())
.create();
Map<String, Object> mapDeserialized = gson.fromJson(payload, UpperCaseAdapter.TYPE);
System.out.println("Map " + mapDeserialized);
and the output is:
Map {KEY3=value, KEY={KEY2=value2, KEY1=value1}}

GSON deserialize two different object type on same list

I'm trying to consume an API that have a return like:
{
"data":{
"id":"12345",
"name":"Some Name",
"regions":[
"r987",
"r654"
]
}
}
that the region attribute is an List<String> with only the regions ID.
But I can ask to the same request retrieve the full region object, so the json change to:
{
"data":{
"id":"12345",
"name":"Some Name",
"regions":{
"data":[
{
"id":"r987",
"name":"region 1"
},
{
"id":"r654",
"name":"region 2"
}
]
}
}
}
I thought of creating an adapter to always translate the list of Id to return a list with the full object, like
class RegionListAdapter implements JsonDeserializer<List<Region>> {
#Override
public List<Region> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext jsc) throws JsonParseException {
List<Region> result = new ArrayList<>();
if (json.isJsonObject()) {
JsonObject envelope = json.getAsJsonObject();
JsonArray data = envelope.getAsJsonArray("data");
result = jsc.deserialize(data, typeOfT);
} else {
JsonArray idArray = json.getAsJsonArray();
for(JsonElement e : idArray){
String regionId = e.getAsString();
Region region = new Region(regionId);
result.add(region);
}
}
return result;
}
}
and with this everything work, receiving only the ID or the full object...
The thing is, I have others attributes that work on the same way. Is there any way to make this adapter work generically or do it differently? Or I need to create a Adapter for each object?
The hard part of making this generic is that you need to create new instances of the object type that is in the list. If all the objects are going to use the field named id as what is going in the list version, we can use that to create a new instance. The easiest hack is shown below, where we stuff the string as the id field into a new JsonObject and use Gson to construct the new object.
class IdListAdapter<T> implements JsonDeserializer<List<T>> {
#Override
public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext jsc) throws JsonParseException {
if (json.isJsonObject()) {
JsonObject envelope = json.getAsJsonObject();
JsonArray data = envelope.getAsJsonArray("data");
return jsc.deserialize(data, typeOfT);
} else {
JsonArray idArray = json.getAsJsonArray();
List<T> result = new ArrayList<T>(idArray.size());
for (JsonElement id : idArray) {
if (typeOfT instanceof ParameterizedType) {
Type parameterType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
JsonObject obj = new JsonObject();
obj.add("id", id);
T element = jsc.deserialize(obj, parameterType);
result.add(element);
}
}
return result;
}
}
}

json into list with Gson

I have a Json like below:
{
"searchResults": {
"searchCriteria": {
"location": {
"originalLocation": null
},
"startAndEndDate": {
"start": "2016-10-06T00:00:00",
"end": "2016-10-09T00:00:00"
},
"solution": [
{
"resultID": "O1MDc1MD",
"selected": false,
"charges": {
"localCurrencyCode": "USD",
"averagePricePerNight": 153
},
"starRating": 3.5
},
{
"resultID": "0MDc1MD",
"selected": false,
"charges": {
"localCurrencyCode": "USD",
"averagePricePerNight": 153
},
"starRating": 3.5
}
....
I have class with attributes starRating and averagePricePerNight which essentially formulates into my POJO.
class ResponseModel {
Int starRating; Int averagePricePerNight
}
I want to parse this JSON and return a List containing :
List(ResponseModel(3.5,900), ResponseModel(3.5,100), ResponseModel(4.5,1000))
I tried to get the json as a List but then i am unable to find examples to get two elements from JSon.
You can write a custom deserializer:
class Deserializers {
public static ResponseModel responseModelDeserializer(JsonElement json, Type typeOfT,
JsonDeserializationContext context) {
JsonObject obj1 = json.getAsJsonObject();
JsonObject obj2 = obj1.get("charges").getAsJsonObject();
double starRating = obj1.get("starRating").getAsDouble();
int averagePricePerNight = obj2.get("averagePricePerNight").getAsInt();
return new ResponseModel(starRating, averagePricePerNight);
}
}
Register it when building Gson:
Gson gson = new GsonBuilder()
.registerTypeAdapter(ResponseModel.class,
(JsonDeserializer<ResponseModel>) Deserializers::responseModelDeserializer
// ^^^ Cast is needed because the parameter has type Object
)
.create();
(Other options include, besides a method reference, are; lambda, anonymous class, or just a regular class. But this one is my favourite.)
Parse your json:
// Get root json object
JsonObject root = new JsonParser().parse(input).getAsJsonObject();
Type tt = new TypeToken<List<ResponseModel>>() {}.getType();
// Get array
List<ResponseModel> mo = gson.fromJson(root.get("solution"), tt);
System.out.println(mo); // [3.5 : 153, 3.5 : 153]
Where ResponseModel is:
class ResponseModel {
private final double starRating;
private final int averagePricePerNight;
public ResponseModel(double starRating, int averagePricePerNight) {
this.starRating = starRating;
this.averagePricePerNight = averagePricePerNight;
}
#Override
public String toString() {
return String.format("%s : %s", starRating, averagePricePerNight);
}
}
I made starRating a double since it seems to be one in your example.

Parse JSON tag name and value to map using Retrofit

I need to parse such kind of JSON:
{
"commodities": {
"39": "GOLD",
"41": "SILVER",
"42": "PLATINUM-APR16",
"85": "SUGAR (11) ",
"108": "WHEAT",
"116": "OIL-MAR16 (WTI CRUDE)",
"130": "CORN ",
"158": "COFFEE ",
"180": "ORANGE S.A.",
"282": "GOLD/JPY",
"304": "GOLD/EUR",
"332": "GOLD/TRY",
"468": "CRB INDEX",
"508": "COPPER",
...and a LOT more...
},
"currencies": {
"2": "USD/JPY",
"35": "AUD/USD",
"38": "USD/ILS",
...and a LOT more...
},
How is it possible to save this JSON to Map? So I could use it like this:
String value = mapCommodities.get(key);
String value = mapCommodities.get(39) //value equals "GOLD"
The problem is I don't know how to parse this index tag from JSON as integer value. I think it's needed to write custom Deserealizer but not really have an idea how.
create a custom deserializer
public class CustomDeserializer implements JsonDeserializer<List<Map<Integer, String>>>{
#Override
public List<Map<Integer, String>> deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<Map<Integer, String>> randomList = new ArrayList<>();
JsonObject parentJsonObject = element.getAsJsonObject();
Map<Integer, String> childMap;
for(Map.Entry<String, JsonElement> entry : parentJsonObject.entrySet()){
childMap = new HashMap<>();
for(Map.Entry<String, JsonElement> entry1 : entry.getValue().getAsJsonObject().entrySet()){
childMap.put(Integer.parseInt(entry1.getKey()), entry1.getValue().toString());
}
randomList.add(childMap);
}
return randomList;
}
}
use it by
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<ArrayList<Map<Integer, String>>>() {}.getType(), new CityListDeserializer());
Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
List<Map<Integer, String>> randomList = gson.fromJson(String.valueOf(object), new TypeToken<ArrayList<Map<Integer, String>>>() {}.getType());
you can use it by
randomList.get(index).get(39);
If you want the it Map<Map<Integer, String>>, that can also be done. Will update that also. But I would't recomment that for very large data set. HashMaps will consume a considerable amount of memory
EDIT:
you can do it this way also
public class CityListDeserializer implements JsonDeserializer<Map<String, Map<Integer, String>>>{
#Override
public Map<String, Map<Integer, String>> deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Map<String, Map<Integer, String>> randomList = new HashMap<>();
JsonObject parentJsonObject = element.getAsJsonObject();
Map<Integer, String> childMap;
for(Map.Entry<String, JsonElement> entry : parentJsonObject.entrySet()){
childMap = new HashMap<>();
for(Map.Entry<String, JsonElement> entry1 : entry.getValue().getAsJsonObject().entrySet()){
childMap.put(Integer.parseInt(entry1.getKey()), entry1.getValue().toString());
}
randomList.put(entry.getKey(), childMap);
}
return randomList;
}
}
use it
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<Map<String, Map<Integer, String>>>() {}.getType(), new CityListDeserializer());
Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
Map<String, Map<Integer, String>> randomList = gson.fromJson(String.valueOf(object), new TypeToken<Map<String, Map<Integer, String>>>() {}.getType());
access the value by
randomList.get("commodities").get(39);
this will return you GOLD
All this was for normal json parsing. Not sure but I guess just giving the typetoken like I gave will make it work for Retrofit also
This is what you can do :)
First convert the response to JSONARRAY using
JSONArray jsonArray = new JSONArray("your string");
Then you can iterate or because you know the structre of the respobnse you can simply access it like :)
JSON commodityJSON = jsonArray.getJSONObject(0);
JSON currencies = jsonArray.getJSONObject(1);
Once you get the JSON objects access it using
commodityJSON.getString("39");
commodityJSON.getString("41");
EDIT
As per your comment :) You can do something like this i believe :)
for (int i = 0; i < jsonArray.length(); ++i) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
Iterator<String> objectKeys = jsonObject.keys();
for( String s : yourKeys){
System.out.println(jsonObject.getString(s));
}
}
Will it help buddy :) Happy coding buddy :)

Categories