I'm trying to build the following json object in java dynamically, for example, if the WARN object doesn't exist, add it or any other one followed by adding a new label message object to the sub array.
This is an example of what I'm trying to dynamically build.
{
"warnings" : [
{
"WARN" : [
{
"label" : "Label Goes Here",
"message" : "Message Goes Here"
},
{
"label" : "Label Goes Here2",
"message" : "Message Goes Here2"
}
],"title" : "Please review the following warnings"
},
{
"NOTIFICATION" : [
{
"label" : "Label Goes Here3",
"message" : "Message Goes Here3"
},
{
"label" : "Label Goes Here4",
"message" : "Message Goes Here4"
}
],"title" : "Please review the following warnings"
}
]
}
This is the what I've tried.
public class Warning {
warningTypes = new JSONObject();
}
private JSONObject warningTypes;
public Warning() {
public Warning(WarningType warningType, String label, String message) {
this.warningType = warningType;
this.label = label;
this.message = message;
}
public void add(WarningType warningType, String label, String message) {
addToJSON(warningType, new JSONObject("label",label,"message",message));
}
private void addToJSON(WarningType warningType, JSONObject jsonObj) {
if(warningTypes.has(warningType.name())) {
JSONArray array = warningTypes.getJSONArray(warningType.name());
array.put(jsonObj);
} else {
warningTypes.put(warningType.name(), new JSONArray(jsonObj));
}
}
public JSONObject toJSON() {
return new JSONObject("warnings", new JSONArray(warningTypes));
}
}
However this is my outcome which you can see is incorrect. I'm unable to add the title do to the fact my warningTypes are being but into a single object.
{
"warnings" : [
{
"WARN" : [
{
"label" : "Label Goes Here",
"message" : "Message Goes Here"
},
{
"label" : "Label Goes Here2",
"message" : "Message Goes Here2"
}
],
"NOTIFICATION" : [
{
"label" : "Label Goes Here3",
"message" : "Message Goes Here3"
},
{
"label" : "Label Goes Here4",
"message" : "Message Goes Here4"
}
]
}
]
}
I can not figure out how to build this object dynamically, any help would be appreciated.
The JSON you are trying to create does not have the same key. Following code will give you the desired output. Refactor the parts into methods as necessary.
Code:
public static class Message {
private String label;
private String message;
public String getMessage() {
return message;
}
public String getLabel() {
return label;
}
public void setMessage(String message) {
this.message = message;
}
public void setLabel(String label) {
this.label = label;
}
}
public static enum WarningType {
WARN, NOTIFICATION
}
public static class Warning {
WarningType type;
List<Message> messages;
String title;
public WarningType getType() {
return type;
}
public void setType(WarningType type) {
this.type = type;
}
public void setMessages(List<Message> messages) {
this.messages = messages;
}
public void setTitle(String title) {
this.title = title;
}
public List<Message> getMessages() {
return messages;
}
public String getTitle() {
return title;
}
}
public static class Warnings {
List<Map<String, Object>> warnings;
public List<Map<String, Object>> getWarnings() {
return warnings;
}
public void setWarnings(List<Map<String, Object>> warnings) {
this.warnings = warnings;
}
public void setWarningsInMap(List<Warning> warningList) {
warnings = new ArrayList<>();
for(Warning each : warningList) {
Map<String, Object> m = new LinkedHashMap<>();
m.put(each.getType().name(), each.getMessages());
m.put("title", each.getTitle());
warnings.add(m);
}
}
}
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
List<Warning> warningList = new ArrayList<>();
Warning warn = new Warning();
warn.setType(WarningType.WARN);
List<Message> warnMessages = new ArrayList<>();
Message m = new Message();
m.setLabel("Label Goes Here");
m.setMessage("Message Goes Here");
warnMessages.add(m);
m = new Message();
m.setLabel("Label Goes Here2");
m.setMessage("Message Goes Here2");
warnMessages.add(m);
warn.setMessages(warnMessages);
warn.setTitle("Please review the following warnings");
warningList.add(warn);
Warning notification = new Warning();
notification.setType(WarningType.NOTIFICATION);
List<Message> notificationMessages = new ArrayList<>();
m = new Message();
m.setLabel("Label Goes Here3");
m.setMessage("Message Goes Here3");
notificationMessages.add(m);
m = new Message();
m.setLabel("Label Goes Here4");
m.setMessage("Message Goes Here4");
notificationMessages.add(m);
notification.setMessages(notificationMessages);
notification.setTitle("Please review the following warnings");
warningList.add(notification);
Warnings w = new Warnings();
w.setWarningsInMap(warningList);
String s = new ObjectMapper().defaultPrettyPrintingWriter().writeValueAsString(w);
System.out.println(s);
}
Output:
{
"warnings" : [ {
"WARN" : [ {
"message" : "Message Goes Here",
"label" : "Label Goes Here"
}, {
"message" : "Message Goes Here2",
"label" : "Label Goes Here2"
} ],
"title" : "Please review the following warnings"
}, {
"NOTIFICATION" : [ {
"message" : "Message Goes Here3",
"label" : "Label Goes Here3"
}, {
"message" : "Message Goes Here4",
"label" : "Label Goes Here4"
} ],
"title" : "Please review the following warnings"
} ]
}
Here's the link to a comprehensive guide.
If you want less work, you can use Google GSON to serialize POJOs to JSON objects. GSON can serialize POJOs, Arrays, Lists, Maps, other collections and will automatically differentiate between numbers and strings. This allows for better object oriented designs and lets you defer serializing to JSON objects up to the GSON. You don't have to worry about dynamically modifying JSONs, just dynamically modify the POJOs. Say you have some objects:
public class Warnings {
#SerializedName("WARN")
private Warn[] warns;
#SerializedName("NOTIFICATION")
private Notification[] notifications;
public WARN[] getWARN(){...}
public void setWarn(WARN[] warns){...}
...
}
public class Warn {
private String message;
private String label;
... // setters & getters
}
public class Notification {
private String message;
private String label;
... // setters & getters
}
you get the point
Then you can serialize it so:
Warnings someWarnings = new Warnings();
// Populate warnings
Gson gson = new Gson();
// Serialize
String jsonString = gson.toJson(someWarnings);
// Deserialize
Warnings sameWarinings = gson.fromJson(jsonString);
Related
I am currently common api client util using spring rest template.
Our api result like following..
single result :
{
"data" : {
"id" : "...",
"name" : "..."
...
}
}
multiple result :
{
"data" : {
"cnt" : 30,
"list" : [
{
"id" : "...",
"name" : "..."
...
},
{
"id" : "...",
"name" : "..."
...
},
{
"id" : "...",
"name" : "..."
...
}
...
]
}
}
And I made two common response class like following
public class Response<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
and
public class ListResponse<T> {
private long cnt;
private List<T> list;
public long getCnt() {
return cnt;
}
public void setCnt(long cnt) {
this.cnt = cnt;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
and example of using RestTemplate is
public <T> T apiCaller(String api){
T result= restTemplate.exchange(api,
HttpMethod.GET,
new HttpEntity<>(headers),
new ParameterizedTypeReference<Response<T>>(){}).getBody().getData();
return result;
}
and then I used,
when the result is single,
UserResponse user = apiRequest.<UserResponse>apiCaller("/users/1");
when the result is multiple,
ListResponse<UserResposne> users = apiRequest.<ListResponse<UserResponse>>apiCaller("/users");
But it also doesn't work.
it occur java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.joont.Response.ListResponse.
So I searched so much in google, but I didn't search how...
How can I solve this problem?
and I did it wrong? is it impossible?
In our project we parse JSON with Jackson. We set field saved by field channelId. Problem is that channelId field is parsed later than saved. So at the time we want to set field saved field channelId is null. How we can set field dependency in JSON deserialization, so field saved will be set after channelId?
This is part of our JSON data:
"message":{
"data":{
"text":"Some text"
},
"saved_by":[
2715,
1234
],
"some_boolean_field":false,
"channel_id":8162
}
This is our entity class:
#JsonIgnoreProperties(ignoreUnknown = true)
#org.parceler.Parcel(org.parceler.Parcel.Serialization.BEAN)
public class Message {
#JsonProperty("channel_id")
protected long channelId;
protected boolean saved;
#JsonSetter("saved_by")
public void setSavedBy(Set<Long> savedBy) {
saved = savedBy.contains(getUserIdByChannelId(channelId));
}
public long getChannelId() {
return channelId;
}
public void setChannelId(long channelId) {
this.channelId = channelId;
}
public boolean isSaved() {
return saved;
}
public void setSaved(boolean saved) {
this.saved = saved;
}
public void setData(JsonNode data) throws JsonProcessingException {
JsonNode textNode = data.get("text");
text = textNode != null ? textNode.asText() : "";
components = new ArrayList<>();
JsonNode mediaNode = data.get("media");
if (mediaNode != null) {
MessageComponent[] parsedComponents = AppSession.getInstance().getObjectMapper().treeToValue(mediaNode, MessageComponent[].class);
List<MessageComponent> components = Arrays.asList(parsedComponents).subList(0, parsedComponents.length < 4 ? parsedComponents.length : 4);
this.components.addAll(components);
}
mediaCount = components.size();
}
}
Full JSON:
{
"data":{
"serial":66,
"updated_entity":"bookmark",
"bookmark":{
"message":{
"data":{
"text":"hello"
},
"counted_serial":748,
"saved_by":[
26526,
27758
],
"type":"UserMessage",
"is_reviewed":false,
"channel_id":8128,
"id":2841531,
"replied_message_data":null,
"is_blocked":false,
"is_deleted":false,
"updated_at":"2016-11-21T05:59:52.471Z",
"spam_reported_by":[
],
"created_at":"2016-11-19T15:40:17.027Z",
"uuid":"0b6ba58e-f5e1-4ee5-a9da-041dfc2c85cd",
"liked_by":[
],
"user":{
"last_name":"M",
"id":4537,
"first_name":"John",
"is_deleted":false,
"avatar_thumb":"https:\/\/cdn.site.org\/uploads\/99ef4d68-6eaf-4ba6-aafa-74d1cf895d71\/thumb.jpg"
},
"serial":934
},
"id":6931,
"created_at":"2016-11-21T05:59:52.459Z",
"is_deleted":false,
"updated_at":"2016-11-21T05:59:52.459Z"
}
},
"type":"action_performed"
}
It's a bit hackish, but by making the Message class its own deserialization-builder, you get a kind of "ready for bean creation"-event in which you have access to all of the properties.
My suggestion is that you try the following :
#JsonDeserialize(builder = Message.class)
public class Message {
...
#JsonSetter("saved_by")
public void setSavedBy(Set<Long> savedBy) {
// Merely store the value for later use.
this.savedBy = savedBy;
}
...
public Message build() {
// Calculate value of "saved" field.
this.saved = this.savedBy.contains(getUserIdByChannelId(this.channelId));
return this;
}
// Handling the added challenge.
#JsonProperty("data")
public void setData(JsonNode data) throws JsonProcessingException {
...
}
}
The above takes advantage of the default settings of the JsonPOJOBuilder annotation, namely that the default value for buildMethodName is build.
I have two different Json responses(having different keys) generated out of two different requests :
Response 1 :
{
"response": {
"count": 2,
"programs": [
{
"title": "xyz1",
"desc": "ABCDEF1"
},
{
"title": "xyz2",
"desc": "ABCDEF2"
}
]
}
}
Response 2
{
"response": {
"count": 3,
"shows": [
{
"name": "PQR1",
"desc": "qwerty1"
},
{
"name": "PQR2",
"desc": "qwerty2"
},
{
"name": "PQR3",
"desc": "qwerty3"
}
]
}
}
As we can see the responses contain data with different keys. But Ultimately It could be transformed into (Array of) same Java object like this one:
Program {
String title;
int description;
}
I want to write single parsing logic that handles different key names and return Program list. How to achieve this efficiently?
Is there any library available to conveniently do this ?
You may choose the field in the getter when deserialized both of them (example works with GSON):
class Program {
private String title, name;
#SerializedName("desc") private String description;
private String getTitle() {
return title == null ? name : title;
}
// other getters, empty constructor and so on...
}
Also (again GSON), you can register your own TypeAdapter when creating Gson object.
// let Program have empty constructor (or no constructors at all), getters and setters
class ProgramAdapter extends TypeAdapter<Program> {
#Override
public Program read(final JsonReader in) throws IOException {
final Program obj = new Program();
in.beginObject();
while (in.hasNext()) {
String jsonTag = in.nextName();
if ("desc".equals(jsonTag)) {
obj.setDescription(in.nextString());
} else if ("title".equals(jsonTag)
|| "name".equals(jsonTag)) {
obj.setTitle(in.nextString());
}
}
in.endObject();
return obj;
}
#Override
public void write(final JsonWriter out, final Program obj)
throws IOException {
out.beginObject();
out.name("title").value(obj.getTitle());
out.name("desc").value(obj.getDescription());
out.endObject();
}
}
// then, when create `Gson` object:
Gson gson = new GsonBuilder().registerTypeAdapter(Program.class, new ProgramAdapter()).create();
I have my original JSON String like this in which I have key and value as shown below -
{
"u":{
"string":"1235"
},
"p":"2047935",
"client_id":{
"string":"5"
},
"origin":null,
"item_condition":null,
"country_id":{
"int":3
},
"timestamp":{
"long":1417823759555
},
"impression_id":{
"string":"2345HH*"
},
"is_consumerid":true,
"is_pid":false
}
As an example, one key is "u" and its value is -
{
"string":"1235"
}
Similarly another key is "country_id" and its value is -
{
"int":3
}
Now what I need to do is, I need to represent key value pair as shown below. If any value is string data type (like value for key u), then represent it's value in double quotes, otherwise don't represent it's value in double quotes. Meaning value of country_id won't be in String double quotes since it is an int.
"u": "1235"
"p": "2047935"
"client_id": "5"
"origin":null
"item_condition":null
"country_id": 3 // I don't have double quotes here around 3 since country_id was int that's why
"timestamp": 1417823759555
"impression_id": "2345HH*"
"is_consumerid": true
"is_pid": false
And then I need to make another json string which should look like this -
{
"u": "1235",
"p": "2047935",
"client_id": "5",
"origin":null,
"item_condition":null,
"country_id": 3,
"timestamp": 1417823759555,
"impression_id": "2345HH*",
"is_consumerid": true,
"is_pid": false
}
So I started with below code but not able to understand what should I do further?
String response = "original_json_string";
Type type = new TypeToken<Map<String, Object>>() {}.getType();
JsonObject jsonObject = new JsonParser().parse(response).getAsJsonObject();
for (Map.Entry<String, JsonElement> object : jsonObject.entrySet()) {
if (object.getValue() instanceof JsonObject) {
String data = object.getValue().toString();
// now not sure what should I do here?
}
}
And my new json should print out like this after serializing.
{
"u": "1235",
"p": "2047935",
"client_id": "5",
"origin":null,
"item_condition":null,
"country_id": 3,
"timestamp": 1417823759555,
"impression_id": "2345HH*",
"is_consumerid": true,
"is_pid": false
}
What is the best way to achieve this?
Note that I'm not yet very experienced with Gson, so there might be easiest ways to do it. Also this solution comes up after the discussion we had previously.
Basically the problem was to get the wanted type in the json file back (which is done by the addEntry method) and each #event key should have its own JSON string (done by computeJson). Since there are only two nested levels, it's fine to do it like that. Otherwise a recursive approach will do the trick.
So if you have only one nested level, you should iterate other the JsonObject's entries'. For each entries, computeJson will add a new Json entry in the List which corresponds to each #event key.
public class Test {
public static void main(String[] args) throws Exception {
List<String> output = new ArrayList<>();
JsonObject jsonObject = new JsonParser().parse(new FileReader("myJson.json")).getAsJsonObject();
for (Map.Entry<String, JsonElement> object : jsonObject.entrySet()) {
if (object.getValue() instanceof JsonObject) {
output.add(computeJson((JsonObject)object.getValue()));
}
}
System.out.println(output);
}
private static String computeJson(JsonObject source) {
JsonObject output = new JsonObject();
for (Map.Entry<String, JsonElement> object : source.entrySet()) {
if (object.getValue() instanceof JsonObject) {
for(Map.Entry<String, JsonElement> entry : ((JsonObject)object.getValue()).entrySet()) {
addEntry(object.getKey(), output, entry);
}
} else {
addEntry(object.getKey(), output, object);
}
}
Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
return gson.toJson(output);
}
private static void addEntry(String key, JsonObject output, Map.Entry<String, JsonElement> object) {
switch(object.getKey().toLowerCase()) {
case "string":
output.addProperty(key, object.getValue().getAsString());
break;
case "int":
output.addProperty(key, object.getValue().getAsInt());
break;
case "long":
output.addProperty(key, object.getValue().getAsLong());
break;
//add other primitive cases
default:
output.add(key, object.getValue());
}
}
}
As described here RawCollectionsExample you can manually parse the json and set it in the desired object. Once values are parsed and set you can again serialize the java object to have desired json.
For setting values from your json you need to have POJO shown below.
public class CustomObject {
private String u;
private String p;
private String client_id;
private String origin;
private String item_condition;
private int country_id;
private long timestamp;
private String impression_id;
private boolean is_consumerid;
private boolean is_pid;
public String getU() {
return u;
}
public void setU(String u) {
this.u = u;
}
public String getP() {
return p;
}
public void setP(String p) {
this.p = p;
}
public String getClient_id() {
return client_id;
}
public void setClient_id(String clientId) {
client_id = clientId;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getItem_condition() {
return item_condition;
}
public void setItem_condition(String itemCondition) {
item_condition = itemCondition;
}
public int getCountry_id() {
return country_id;
}
public void setCountry_id(int countryId) {
country_id = countryId;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public String getImpression_id() {
return impression_id;
}
public void setImpression_id(String impressionId) {
impression_id = impressionId;
}
public boolean isIs_consumerid() {
return is_consumerid;
}
public void setIs_consumerid(boolean isConsumerid) {
is_consumerid = isConsumerid;
}
public boolean isIs_pid() {
return is_pid;
}
public void setIs_pid(boolean isPid) {
is_pid = isPid;
}
#Override
public String toString() {
return "CustomObject [client_id=" + client_id + ", country_id="
+ country_id + ", impression_id=" + impression_id
+ ", is_consumerid=" + is_consumerid + ", is_pid=" + is_pid
+ ", item_condition=" + item_condition + ", origin=" + origin
+ ", p=" + p + ", timestamp=" + timestamp + ", u=" + u + "]";
}
}
In above POJO you can parse and set JSON value manually as shown below :
String jsonLine = "{ \"u\":{ \"string\":\"1235\" }, \"p\":\"2047935\", \"client_id\":{ \"string\":\"5\" }, \"origin\":null, \"item_condition\":null, \"country_id\":{ \"int\":3 }, \"timestamp\":{ \"long\":1417823759555 }, \"impression_id\":{ \"string\":\"2345HH*\" }, \"is_consumerid\":true, \"is_pid\":false}";
JsonParser parser = new JsonParser();
//in case you have json array you need to use .getAsJsonArray instead of getAsJsonObject
JsonObject jsonObject = parser.parse(jsonLine).getAsJsonObject();
CustomObject obj = new CustomObject();
obj.setP(jsonObject.get("p").getAsString());
obj.setU(jsonObject.get("u").getAsJsonObject().get("string").getAsString());
obj.setClient_id(jsonObject.get("client_id").getAsJsonObject().get("string").getAsString());
//null check which will be required for each value in case there are possibility of having null values
String origin = jsonObject.get("origin").isJsonNull() ==true?null:jsonObject.get("origin").getAsString();
obj.setOrigin(origin);
String itemCondition = jsonObject.get("item_condition").isJsonNull() ==true?null:jsonObject.get("item_condition").getAsString();
obj.setItem_condition(itemCondition);
obj.setCountry_id(jsonObject.get("country_id").getAsJsonObject().get("int").getAsInt());
obj.setTimestamp(jsonObject.get("timestamp").getAsJsonObject().get("long").getAsLong());
obj.setImpression_id(jsonObject.get("impression_id").getAsJsonObject().get("string").getAsString());
obj.setIs_consumerid(jsonObject.get("is_consumerid").getAsBoolean());
obj.setIs_pid(jsonObject.get("is_consumerid").getAsBoolean());
System.out.println("JSON OUTPUT "+ new Gson().toJson(obj));
You can run the code snippet in any class's main method to validate. Check the last line above which outputs required json. Let me know if this is not what you were looking for.
I have a JsonNode which contains the following JSON. Inside that JsonNode object is an array. In that array there are three fields, one of which, slaid, is a list. The other two are strings. Here is the JSON.
{
"SLA": [
{
"slaid": [
"53637cc144ae8b607e089701"
],
"ragindicator": "Red",
"name": "r1"
},
{
"slaid": [
"53637d1844ae8b607e089704"
],
"ragindicator": "Amber",
"name": "a1"
},
{
"slaid": [
"53637eac44ae8b607e089706"
],
"ragindicator": "Green",
"name": "g1"
}
]
}
I want to parse this value. How can I parse it , where slaid's type is List<String>? I have tried some ways but I am still unable to find the solution.
The easiest way I can see is creating POJO classes which fit to your JSON:
class Slaids {
#JsonProperty("SLA")
private List<Slaid> slaids;
public List<Slaid> getSlaids() {
return slaids;
}
public void setSlaids(List<Slaid> slaids) {
this.slaids = slaids;
}
#Override
public String toString() {
return slaids.toString();
}
}
class Slaid {
private List<String> slaid;
private String ragindicator;
private String name;
public List<String> getSlaid() {
return slaid;
}
public void setSlaid(List<String> slaid) {
this.slaid = slaid;
}
public String getRagindicator() {
return ragindicator;
}
public void setRagindicator(String ragindicator) {
this.ragindicator = ragindicator;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Slaid [slaid=" + slaid + ", ragindicator=" + ragindicator + ", name=" + name + "]";
}
}
Simple usage:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json, Slaids.class));
Above program prints:
[Slaid [slaid=[53637cc144ae8b607e089701], ragindicator=Red, name=r1], Slaid [slaid=[53637d1844ae8b607e089704], ragindicator=Amber, name=a1], Slaid [slaid=[53637eac44ae8b607e089706], ragindicator=Green, name=g1]]
If you want to use JsonNode you can do it in this way:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
ArrayNode slaidsNode = (ArrayNode) rootNode.get("SLA");
Iterator<JsonNode> slaidsIterator = slaidsNode.elements();
while (slaidsIterator.hasNext()) {
JsonNode slaidNode = slaidsIterator.next();
System.out.println(slaidNode.get("slaid"));
System.out.println(slaidNode.get("ragindicator"));
System.out.println(slaidNode.get("name"));
}
Above program prints:
["53637cc144ae8b607e089701"]
"Red"
"r1"
["53637d1844ae8b607e089704"]
"Amber"
"a1"
["53637eac44ae8b607e089706"]
"Green"
"g1"