I am trying to send a custom payload to a String in Java. In this case, a JSON Object.
Ordinarily, if I wanted to send a decimal to a string I might do something like this
String myDecimal = "my decimal is %d";
System.out.println(String.format(myDecimal, 20)); // print "my decimal is 20)
However, instead of sending a decimal, or a string (%s), I would like to send a JSON object. Is this possible, if so how?
Probably with Java 15 text blocks, it can be simplified a little bit:
private static String EXT_EVENT = """
{
"version": "1.0",
"method": "Event",
"target": "ExampleTarget",
"name": "OnEventReceived",
"payload": {
"header": %1$s,
"endpoint": %2$s,
"payload": %3$s
}
}
""";
String example = String.format(EXT_EVENT, "test1", "test2", "test3");
Or even with Java template strings and string interpolation (to be introduced probably in Java 21):
private static String h = "test1";
private static String e = "test2";
private static String p = "test3";
private static String example = """
{
"version": "1.0",
"method": "Event",
"target": "ExampleTarget",
"name": "OnEventReceived",
"payload": {
"header": \{h},
"endpoint": \{e},
"payload": \{p}
}
}
""";
As far as I know, you cannot pass an object. so far, my best solution has been passing strings formatted to be like a JSON object, then parsing it into a JSON Object.
Example
class Main {
private static String EXT_EVENT = "{" +
" \"version\": \"1.0\"," +
" \"method\": \"Event\"," +
" \"target\": \"ExamppleTarget\"," +
" \"name\": \"OnEventReceived\"," +
" \"payload\": { \"header\": %1$s," +
" \"endpoint\": %2$s, " +
" \"payload\": %3$s }" +
"}";
public static void main(String[] args) {
String example = String.format(EXT_EVENT,"test1", "test2", "test3");
System.out.println(example);
}
}
Related
I have an API which returns an array of 2 objects. My problem is that each object has a different type. I can deserialize it if its 1 element each but when it returns multiple elements with different types I'm struggling to find out how to do that. An example of the JSON is below. One possible way to deserialize is based on the array index because we can guarantee the order and thus force a type. Another is based on the result of the path key which will always the return the same value for each element.
[
{
"path": "matter",
"result": {
"criticalDates.dateClosed": {
"id": "-2",
"name": "Date Closed",
"confirmed": false,
"confirmStatus": "Complete",
"order": 2,
"status": "Complete",
"isConfirmable": true,
"displayName": "Date Closed",
"autoCalc": false,
"__id": "e9d-4329-bb4a-03e644afdfda",
"__className": "CriticalDate",
"__tableId": "-24",
"__classes": [
"CriticalDate"
],
"date": null
},
"matterType": "Family",
"personActing.fullName": "Michael"
},
"status": "ok"
},
{
"path": "matter.cardList",
"result": [
{
"person.firstNames": "Daniel Testing",
"person.lastName": "Lastname"
},
{
"person.firstNames": "Daniel Testing",
"person.lastName": "Lastname"
}
],
"status": "ok"
}
]
What would be the appropriate way to deserialize this? Is there an annotation only approach?
One option is to use JsonTypeInfo to determine the target class.
Sample DTOs:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "path")
#JsonSubTypes({#JsonSubTypes.Type(value = MatterPath.class, name = "matter"),
#JsonSubTypes.Type(value = CardListPath.class, name = "matter.cardList")})
public abstract class AbstractPath {
private String path;
// Getters and Setters
}
//------------------------------
public class MatterPath extends AbstractPath {
private String matterType;
// Other fields, getter and setters
}
//---------------------------------
public class CardListPath extends AbstractPath{
private String cardListType;
}
Explanation:
#JsonTypeInfo - Use this annotation to determine the subclass based on the existing property. In our case path. For further details refer here.
#JsonSubTypes - Use this annotation to map the value in path field and the destination class to be used. For details refer JsonSubTypes.
Testing:
String json = "[\n" +
" {\n" +
" \"path\": \"matter\",\n" +
" \"matterType\": \"Family\"\n" +
" },\n" +
" {\n" +
" \"path\": \"matter.cardList\",\n" +
" \"cardListType\": \"ok\"\n" +
" }\n" +
"]\n";
AbstractPath[] abstractPaths = objectMapper.readValue(json, AbstractPath[].class);
System.out.println(Arrays.toString(abstractPaths));
Output:
[MatterPath{matterType='Family'}, CardListPath{cardListType='ok'}]
I have a JSON with some data and I would like to print as follows
10 REGISTER 1, KP SUM 2081,606
20 REGISTER 2 CH SUM 0,22
Where the general sum is calculated by the total sum of the items according to the code.
Following the rule, first multiply the quantity by the unit and then add all the items that have the same code.
Example:
code 10
SUM = 0,0200000 * 7,40 + 10,0000000 * 200,31 + 0,5690000 * 40,19 + 0,7890000 * 70,33
The same goes for the other codes that appear in JSON
My JSON
[
{
"code": 10,
"description": "REGISTER 1",
"unity": "KP",
"typeItem": "I",
"itemCode": 1,
"descriptionItem": "ITEM",
"unityItem": "UN",
"quantity": "0,0200000",
"valueUnity": "7,40"
},
{
"code": 10,
"description": "REGISTER 1",
"unity": "KP",
"typeItem": "I",
"codeItem": 2,
"descriptionItem": "ITEM 2",
"unityItem": "UN",
"quantity": "10,0000000",
"valueUnity": "200,31"
},
{
"code": 10,
"description": "REGISTER 1",
"unity": "KP",
"typeItem": "I",
"codeItem": 88248,
"descriptionItem": "ITEM 3",
"unityItem": "H",
"quantity": "0,5690000",
"valueUnity": "40,19"
},
{
"code": 10,
"description": "REGISTER 1",
"unity": "KP",
"typeItem": "I",
"codeItem": 88267,
"descriptionItem": "ITEM 4",
"unityItem": "N",
"quantity": "0,7890000",
"valueUnity": "70,33"
},
{
"code": 20,
"description": "REGISTER 2",
"unity": "CH",
"typeItem": "I",
"codeItem": 1,
"descriptionItem": "ITEM 1",
"unityItem": "H",
"quantity": "30,0000000",
"valueUnity": "0,17"
},
{
"code": 20,
"description": "REGISTER 2",
"unity": "CH",
"typeItem": "I",
"codeItem": 2,
"descriptionItem": "ITEM 2",
"unityItem": "H",
"quantity": "3,0000000",
"valueUnity": "0,07"
}
]
My class Java
public class MyJson {
#SerializedName("code")
#Expose
private Integer code;
#SerializedName("description")
#Expose
private String description;
#SerializedName("unity")
#Expose
private String unity;
#SerializedName("typeItem")
#Expose
private String typeItem;
#SerializedName("codeItem")
#Expose
private Integer codeItem;
#SerializedName("descriptionItem")
#Expose
private String descriptionItem;
#SerializedName("unityItem")
#Expose
private String unityItem;
#SerializedName("quantity")
#Expose
private String quantity;
#SerializedName("valueUnity")
#Expose
private String valueUnity;
private Double total;
}
My Program
public static void main(String[] args) {
Gson gson = new Gson();
try {
File jsonFile = new File("C:\\my_json.json");
InputStreamReader reader = new InputStreamReader(new FileInputStream(jsonFile), "UTF-8");
BufferedReader jsonBuffer = new BufferedReader(reader);
MyJson[] myJsonArray = gson.fromJson(jsonBuffer, MyJson[].class);
BigDecimal valueUnity = BigDecimal.ZERO;
BigDecimal sumTotal = BigDecimal.ZERO;
//
Set<MyJson> list = new HashSet<>();
for(MyJson myJson : myJsonArray) {
if(checkStringNullOrEmpty(myJson.getQuantity()) && checkStringNullOrEmpty(myJson.getValueUnity())) {
if(myJson.getCode().equals(myJson.getCode())) {
String value1 = myJson.getQuantity().replaceAll( "," , "." ).trim();
String value2 = myJson.getValueUnity.replaceAll( "," , "." ).trim();
BigDecimal quantity = new BigDecimal(value1);
BigDecimal valueUnit = new BigDecimal(value2);
valueUnity = quantity.multiply(valueUnit);
somaTotal = sumTotal.add(valueUnity);
String resultado = String.format(Locale.getDefault(), "%.2f", valueUnity);
String sumTotal2 = String.format(Locale.getDefault(), "%.2f", sumTotal);
myJson.setTotal(new Double(sumTotal2.replaceAll( "," , "." ).trim()));
list.add(myJson);
}
}
}
for(MyJson myJson : list) {
StringBuilder builer = new StringBuilder();
builer.append(myJson.getCode()).append(" ");
builer.append(myJson.getDescription().toUpperCase()).append(" ");
builer.append(myJson.getUnity().toUpperCase()).append(" ");
builer.append(myJson.getTotal());
System.out.println(builer.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static boolean checkStringNullOrEmpty(String value) {
if(!value.isEmpty()) {
return true;
} else {
return false;
}
}
Exit program
The calculation is being done wrong when using the Set
10 REGISTER 1, KP SUM 130,33
20 REGISTER 2 CH SUM 439,18
You cannot keep track of multiple running totals (i.e. one for each code) using one total. Instead you will need one total for each different code.
I would recommend that you use a Map<Integer, MyJson> for this purpose. This would store a number of MyJson objects which you could look up by their code. When handling each MyJson object, you check to see if you already have a MyJson object with the same code: if you do then you add to its total, otherwise you add your MyJson object to the map.
Get rid of your Set<MyJson> variable (which you have somewhat confusingly named list) and replace it with the following
Map<Integer, MyJson> jsonsByCode = new LinkedHashMap<>();
(You can use a HashMap<> instead of a LinkedHashMap<> here: I chose to use a LinkedHashMap<> because it keeps its entries in the same order they were inserted into it.)
Then, replace all lines from somaTotal = sumTotal.add(valueUnity); to list.add(myJson); with
if (jsonsByCode.containsKey(myJson.getCode())) {
// We've seen this code before, so add the value
// to the total.
MyJson matchingJson = jsonsByCode.get(myJson.getCode());
matchingJson.setTotal(matchingJson.getTotal() + valueUnity.doubleValue());
} else {
// First time seeing this code, so set its total
// and add it to the map.
myJson.setTotal(valueUnity.doubleValue());
jsonsByCode.put(myJson.getCode(), myJson);
}
(Note that BigDecimal values such as valueUnity have a .doubleValue() method on them, which is the easiest way to convert them to a double.)
Then, in the for loop below, where you are printing out the values, replace list with jsonsByCode.values().
I made these changes to your program and it generated the following output:
10 REGISTER 1 KP 2081.60648
20 REGISTER 2 CH 5.31
Incidentally, your code also contains the following if statement:
if(myJson.getCode().equals(myJson.getCode())) {
// ....
}
You are comparing myJson.getCode() against itself, so this condition will always be true (unless of course myJson.getCode() returns null, in which case you get a NullPointerException). You can just get rid of this check, it doesn't do anything useful.
I know the answer for parsing the JSON of this type:
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food"}
where there is key value pair, with key being same(like 'id' here) and value differing, and we use a for loop to parse it quickly.
(For those who'd like to see how to parse above JSON, please go to this link: How to parse nested JSON object using the json library?)
However, the JSON I'm trying to parse is a different one which doesn't have a same key like 'Id' as above for every different value, but every key is a new key with a different value. Below is the example:
{
"disclaimer": "Exchange rates are ...........blah blah",
"license": "Data sourced from various .......blah blah",
"timestamp": 1446886811,
"base": "USD",
"rates": {
"AED": 3.67266,
"AFN": 65.059999,
"ALL": 127.896
.
.
All the currency values.
.
}
}
I'm not sure how to parse the above one with all different keys of currencies (currency like AED and their value) and pop them up in a drop down list.
Do I have to write a new line of code for each different currency and value pair or it is in some way possible to use a for loop for this one as well.
Can someone provide some lines code if possible?
you can use org.json for this thing.
E.g.:
JSONObject json = new JSONObject("<jsonString>");
Iterator<String> keys = json.keys();
while (keys.hasNext()) {
String key = keys.next();
System.out.println("Key :" + key + " Value :" + json.get(key));
}
You could use GSON in this case. I will just print the currencies with the according rate but you can build a different data structure(a map for example) and use it in your system.
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.util.Map;
public class Main {
public static void main(String[] args) throws IOException {
String jsonString = "{\n" +
" \"disclaimer\": \"Exchange rates are ...........blah blah\",\n" +
" \"license\": \"Data sourced from various .......blah blah\",\n" +
" \"timestamp\": 1446886811,\n" +
" \"base\": \"USD\",\n" +
" \"rates\": {\n" +
" \"AED\": 3.67266,\n" +
" \"AFN\": 65.059999,\n" +
" \"ALL\": 127.896\n" +
" }\n" +
"}";
JsonObject jsonObject = new JsonParser().parse(jsonString).getAsJsonObject();
for(Map.Entry<String, JsonElement> currency: jsonObject.getAsJsonObject("rates").entrySet()){
System.out.println("Currency "+ currency.getKey()+" has rate " + currency.getValue());
}
}
}
For some reasons I have to use a specific string in my project. This is the text file (it's a JSON File):
{"algorithm":
[
{ "key": "onGapLeft", "value" : "moveLeft" },
{ "key": "onGapFront", "value" : "moveForward" },
{ "key": "onGapRight", "value" : "moveRight" },
{ "key": "default", "value" : "moveBackward" }
]
}
I've defined it in JAVA like this:
static String input = "{\"algorithm\": \n"+
"[ \n" +
"{ \"key\": \"onGapLeft\", \"value\" : \"moveLeft\" }, \n" +
"{ \"key\": \"onGapFront\", \"value\" : \"moveForward\" }, \n" +
"{ \"key\": \"onGapRight\", \"value\" : \"moveRight\" }, \n" +
"{ \"key\": \"default\", \"value\" : \"moveBackward\" } \n" +
"] \n" +
"}";
Now I have to isolate the keys and values in an array:
key[0] = onGapLeft; value[0] = moveLeft;
key[1] = onGapFront; value[1] = moveForward;
key[2] = onGapRight; value[2] = moveRight;
key[3] = default; value[3] = moveBackward;
I'm new to JAVA and don't understand the string class very well. Is there an easy way to get to that result? You would help me really!
Thanks!
UPDATE:
I didn't explained it well enough, sorry. This program will run on a LEGO NXT Robot. JSON won't work there as I want it to so I have to interpret this JSON File as a normal STRING! Hope that explains what I want :)
I propose a solution in several step.
1) Let's get the different parts of your ~JSON String. We will use a pattern to get the different {.*} parts :
public static void main(String[] args) throws Exception{
List<String> lines = new ArrayList<String>();
Pattern p = Pattern.compile("\\{.*\\}");
Matcher matcher = p.matcher(input);
while (matcher.find()) {
lines.add(matcher.group());
}
}
(you should take a look at Pattern and Matcher)
Now, lines contains 4 String :
{ "key": "onGapLeft", "value" : "moveLeft" }
{ "key": "onGapFront", "value" : "moveForward" }
{ "key": "onGapRight", "value" : "moveRight" }
{ "key": "default", "value" : "moveBackward" }
Given a String like one of those, you can remove curly brackets with a call to String#replaceAll();
List<String> cleanLines = new ArrayList<String>();
for(String line : lines) {
//replace curly brackets with... nothing.
//added a call to trim() in order to remove whitespace characters.
cleanLines.add(line.replaceAll("[{}]","").trim());
}
(You should take a look at String String#replaceAll(String regex))
Now, cleanLines contains :
"key": "onGapLeft", "value" : "moveLeft"
"key": "onGapFront", "value" : "moveForward"
"key": "onGapRight", "value" : "moveRight"
"key": "default", "value" : "moveBackward"
2) Let's parse one of those lines :
Given a line like :
"key": "onGapLeft", "value" : "moveLeft"
You can split it on , character using String#split(). It will give you a String[] containing 2 elements :
//parts[0] = "key": "onGapLeft"
//parts[1] = "value" : "moveLeft"
String[] parts = line.split(",");
(You should take a look at String[] String#split(String regex))
Let's clean those parts (remove "") and assign them to some variables:
String keyStr = parts[0].replaceAll("\"","").trim(); //Now, key = key: onGapLeft
String valueStr = parts[1].replaceAll("\"","").trim();//Now, value = value : moveLeft
//Then, you split `key: onGapLeft` with character `:`
String key = keyStr.split(":")[1].trim();
//And the same for `value : moveLeft` :
String value = valueStr.split(":")[1].trim();
That's it !
You should also take a look at Oracle's tutorial on regular expressions (This one is really important and you should invest time on it).
You need to use a JSON parser library here. For example, with org.json you could parse it as
String input = "{\"algorithm\": \n"+
"[ \n" +
"{ \"key\": \"onGapLeft\", \"value\" : \"moveLeft\" }, \n" +
"{ \"key\": \"onGapFront\", \"value\" : \"moveForward\" }, \n" +
"{ \"key\": \"onGapRight\", \"value\" : \"moveRight\" }, \n" +
"{ \"key\": \"default\", \"value\" : \"moveBackward\" } \n" +
"] \n" +
"}";
JSONObject root = new JSONObject(input);
JSONArray map = root.getJSONArray("algorithm");
for (int i = 0; i < map.length(); i++) {
JSONObject entry = map.getJSONObject(i);
System.out.println(entry.getString("key") + ": "
+ entry.getString("value"));
}
Output :
onGapLeft: moveLeft
onGapFront: moveForward
onGapRight: moveRight
default: moveBackward
I have the following JSON:
{
"registration": {
"name": "Vik Kumar",
"first_name": "Vik",
"last_name": "Kumar",
"bloodGroup": "B-",
"gender": "male",
"birthday": "10\/31\/1983",
"email": "vik.ceo\u0040gmail.com",
"cellPhone": "1234123456",
"homePhone": "1234123457",
"officePhone": "1234123458",
"primaryAddress": "jdfjfgj",
"area": "jfdjdfj",
"location": {
"name": "Redwood Shores, California",
"id": 103107903062719
},
"subscribe": true,
"eyePledge": false,
"reference": "fgfgfgfg"
}
}
I am using the following code to parse it:
JsonNode json = new ObjectMapper().readTree(jsonString);
JsonNode registration_fields = json.get("registration");
Iterator<String> fieldNames = registration_fields.getFieldNames();
while(fieldNames.hasNext()){
String fieldName = fieldNames.next();
String fieldValue = registration_fields.get(fieldName).asText();
System.out.println(fieldName+" : "+fieldValue);
}
This works fine and it print all the values except for location which is kind of another level of nesting. I tried the same trick as above code to pass json.get("location") but that does not work. Please suggest how to make it work for location.
You need to detect when you are dealing with a (nested) Object using JsonNode#isObject:
public static void printAll(JsonNode node) {
Iterator<String> fieldNames = node.getFieldNames();
while(fieldNames.hasNext()){
String fieldName = fieldNames.next();
JsonNode fieldValue = node.get(fieldName);
if (fieldValue.isObject()) {
System.out.println(fieldName + " :");
printAll(fieldValue);
} else {
String value = fieldValue.asText();
System.out.println(fieldName + " : " + value);
}
}
}
Thus, when you reach an object, such as location, you'll call the printAll recursively to print all its inner values.
org.codehaus.jackson.JsonNode json = new ObjectMapper().readTree(jsonString);
org.codehaus.jackson.JsonNode registration_fields = json.get("registration");
printAll(registration_fields);
Since location is nested within registration, you need to use:
registration_fields.get("location");
to get it. But isn't it already processed by the while-loop, why do you need to get it separately?