I have some JSON which is subject to change but one constant is it will always contain multiple objects with two properties; text and answerText. An example JSON would be
{
"food": {
"eggTolerance": {
"answerText": "None",
"text": "What is your egg tolerance?"
},
"lactoseTolerance": null
},
"cookingExperience": {
"experienceInLastFiveYears": {
"answerText": "Yes",
"text": "Was this experience within the last 5 years?"
},
"numberOfPies": {
"answerText": "More than 50",
"text": "How many pies have you baked?"
},
"significantPies": {
"answerText": "More than 50",
"text": "How many of these pies per quarter were at tasty?"
},
"spanOfExperience": {
"answerText": "Yes",
"text": "Do you have at least 12 months' experience baking pies?"
}
},
"cocktails": {
"manhattans": {
"answerText": "The kiss of death",
"text": "What have I done to deserve this flat, flavourless Manhattan?"
},
"Gin Martini": null
},
"waitressing": null
}
This can be changed by making it deeper or wider. For example the lactoseTolerance could have another object added to it or another object could be added to the root object.
How can I traverse this object to visit every object to get the properties of the deepest object?
I have seen this example but this just gives me the first level. In this instance I know I can then iterate the children of those objects but if the hierarchy changes the implementation is ruined.
I have used GSON lib:
Please try below code:
pom.xml
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
Then I have created QA class that have your fixed two properties; text and answerText
import com.google.gson.annotations.SerializedName;
public class QA {
#SerializedName("answerText")
private String answerText;
#SerializedName("text")
private String text;
public QA(String answerText, String text) {
this.answerText = answerText;
this.text = text;
}
#Override
public String toString() {
return "QA{" +
"answerText='" + answerText + '\'' +
", text='" + text + '\'' +
'}';
}
public String getAnswerText() {
return answerText;
}
public void setAnswerText(String answerText) {
this.answerText = answerText;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Now in driver code:
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.Map;
public class Main {
public static void main(String[] args) {
System.out.println("running!!");
InputStream inputStream = Main.class.getResourceAsStream("json.json");
String json = "{\n" +
" \"food\": {\n" +
" \"eggTolerance\": {\n" +
" \"answerText\": \"None\",\n" +
" \"text\": \"What is your egg tolerance?\"\n" +
" },\n" +
" \"lactoseTolerance\": null\n" +
" },\n" +
" \"cookingExperience\": {\n" +
" \"experienceInLastFiveYears\": {\n" +
" \"answerText\": \"Yes\",\n" +
" \"text\": \"Was this experience within the last 5 years?\"\n" +
" },\n" +
" \"numberOfPies\": {\n" +
" \"answerText\": \"More than 50\",\n" +
" \"text\": \"How many pies have you baked?\"\n" +
" },\n" +
" \"significantPies\": {\n" +
" \"answerText\": \"More than 50\",\n" +
" \"text\": \"How many of these pies per quarter were at tasty?\"\n" +
" },\n" +
" \"spanOfExperience\": {\n" +
" \"answerText\": \"Yes\",\n" +
" \"text\": \"Do you have at least 12 months' experience baking pies?\"\n" +
" }\n" +
" },\n" +
" \"cocktails\": {\n" +
" \"manhattans\": {\n" +
" \"answerText\": \"The kiss of death\",\n" +
" \"text\": \"What have I done to deserve this flat, flavourless Manhattan?\"\n" +
" },\n" +
" \"Gin Martini\": null\n" +
" },\n" +
" \"waitressing\": null\n" +
"}";
final Gson gson = new Gson();
Type type = new TypeToken<Map<String, Map<String, QA>>>(){}.getType();
Map<String, Map<String, QA>> myMap = gson.fromJson(json, type);
System.out.println("Data:"+ myMap.get("food").get("eggTolerance").getAnswerText());
}
}
Related
I'm new to spring and Java and trying to figure out how to go about formatting the json response into the desired structure.
I have a spring query that's returning 2 columns from a table like below which are really the key and values I need for the json structure:
Names
Values
Car
Toyota
Bike
Schwinn
Scooter
Razor
A0
11
A1
12
A2
13
B0
2000
B1
4000
B2
22000
The current json output from the controller is this:
[{
"names": "Car",
"values": "Toyota"
},
{
"names": "Bike",
"values": "Schwinn"
},
{
"names": "Scooter",
"values": "Razor"
},
{
"names": "A0",
"values": "11"
},
{
"names": "A1",
"values": "12"
},
{
"names": "A2",
"values": "13"
},
{
"names": "B0",
"values": "2000"
},
{
"names": "B1",
"values": "4000"
},
{
"names": "B2",
"values": "22000"
}
]
And the desired json format is this where the table column names are removed and instead json structure is created using the names column for the keys:
{
"Car": "Toyota",
"Bike": "Schwinn",
"Scooter": "Razor",
"Data": [{
"A0": "11",
"B0": "2000"
}, {
"A1": "12",
"B1": "4000"
}, {
"A2": "13",
"B2": "22000"
}]
}
Repository
#Query (value = "Select names, values ... :id")
List<Data> findData(#Param("id") Long id) ;
interface Data {
String getnames();
String getvalues();
}
Service
public List<Data> getData(Long id) {return repo.findData(id);}
Controller
#GetMapping("/getdata/{id}")
public ResponseEntity<List<Data>> getData(#PathVariable Long id) {
List<Data> c = service.getData(id);
return new ResponseEntity<>(c, HttpStatus.OK);
}
It seems that I need to process the result set and need to loop through them to create the desired structure but not sure how to proceed with that, or perhaps there is an easier way to get to the desired structure. Any guidance would be appreciated.
So return a ResponseEntity<Map<String, Object>> instead of a List to simulate a Json object.
List<Data> c = service.getData(id);
Map<String, Object> map = new HashMap<>();
map.put("Key", "Value");
map.put("Car", c.get(0).getvalues());
map.put("Entire List", c);
return new ResponseEntity<>(c, HttpStatus.OK);
Obviously you'll have to write your own logic but it should be pretty straight forward. Or, even better, consider making a class for the object returned if you're going to be using it a lot, and just return ResponseEntity< YourCustomObject >
This looks a bit complicated, I think you should set the primary key association for values like A0 B0
import com.black_dragon.utils.JacksonUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
/**
* #author black_dragon
* #version V1.0
* #Package com.black_dragon.swing
* #date 2022/9/6 10:35
* #Copyright
*/
public class ConvertToMap {
String names;
String values;
public String getNames() {
return names;
}
public void setNames(String names) {
this.names = names;
}
public String getValues() {
return values;
}
public void setValues(String values) {
this.values = values;
}
private static String DIGIT_REGEX = "[^0-9]";
private static String LETTER_DIGIT_REGEX = "[a-zA-Z]+";
public static Integer getDigit(String str){
Pattern pattern = Pattern.compile(DIGIT_REGEX);
if(!isLetterDigit(str)){
String[] keySet = pattern.split(str);
if(keySet.length > 0){
return Integer.valueOf(keySet[1]);
}
}
return -1;
}
public static boolean isLetterDigit(String str){
return str.matches(LETTER_DIGIT_REGEX);
}
private static String fetchGroupKey(ConvertToMap convertToMap){
return String.valueOf(getDigit(convertToMap.names));
}
public static void main(String[] args) {
String jsonString = "[{\n" +
" \"names\": \"Car\",\n" +
" \"values\": \"Toyota\"\n" +
" },\n" +
" {\n" +
" \"names\": \"Bike\",\n" +
" \"values\": \"Schwinn\"\n" +
" },\n" +
" {\n" +
" \"names\": \"Scooter\",\n" +
" \"values\": \"Razor\"\n" +
" },\n" +
" {\n" +
" \"names\": \"A0\",\n" +
" \"values\": \"11\"\n" +
" },\n" +
" {\n" +
" \"names\": \"A1\",\n" +
" \"values\": \"12\"\n" +
" },\n" +
" {\n" +
" \"names\": \"A2\",\n" +
" \"values\": \"13\"\n" +
" },\n" +
" {\n" +
" \"names\": \"B0\",\n" +
" \"values\": \"2000\"\n" +
" },\n" +
" {\n" +
" \"names\": \"B1\",\n" +
" \"values\": \"4000\"\n" +
" },\n" +
" {\n" +
" \"names\": \"B2\",\n" +
" \"values\": \"22000\"\n" +
" }\n" +
"]";
List<ConvertToMap> convertToMaps = JacksonUtils.toJavaList(jsonString, ConvertToMap.class);
// Extract a string that does not contain numbers and convert it to a map
Map<String, Object> result = convertToMaps.stream()
.filter(x -> isLetterDigit(x.names))
.collect(Collectors.toMap(ConvertToMap::getNames, ConvertToMap::getValues));
List<Map<String, String>> mapList = new ArrayList<>();
// Group by string numbers containing numbers
Map<String, List<ConvertToMap>> stringListMap = convertToMaps.stream().collect(groupingBy(convertToMap -> fetchGroupKey(convertToMap)));
for (String key : stringListMap.keySet()) {
if(Integer.valueOf(key) >= 0){
mapList.add(stringListMap.get(key)
.stream()
.collect(Collectors.toMap(ConvertToMap::getNames, ConvertToMap::getValues)));
}
}
result.put("Data", mapList);
System.out.println(JacksonUtils.toJSONString(result));
}
}
Assume that your data key name pattern is one non-digit followed by digits.
https://github.com/octomix/josson
Deserialization
Josson josson = Josson.fromJsonString(
"[" +
" {" +
" \"names\": \"Car\"," +
" \"values\": \"Toyota\"" +
" }," +
" {" +
" \"names\": \"Bike\"," +
" \"values\": \"Schwinn\"" +
" }," +
" {" +
" \"names\": \"Scooter\"," +
" \"values\": \"Razor\"" +
" }," +
" {" +
" \"names\": \"A0\"," +
" \"values\": \"11\"" +
" }," +
" {" +
" \"names\": \"A1\"," +
" \"values\": \"12\"" +
" }," +
" {" +
" \"names\": \"A2\"," +
" \"values\": \"13\"" +
" }," +
" {" +
" \"names\": \"B0\"," +
" \"values\": \"2000\"" +
" }," +
" {" +
" \"names\": \"B1\"," +
" \"values\": \"4000\"" +
" }," +
" {" +
" \"names\": \"B2\"," +
" \"values\": \"22000\"" +
" }" +
"]");
Transformation
JsonNode node = josson.getNode(
"#collect([names !=~ '\\D\\d+']*" +
" .map(names::values)" +
" ,[names =~ '\\D\\d+']*" +
" .group(names.substr(1), map(names::values))#" +
" .elements" +
" .mergeObjects()" +
" .#toObject('Data')" +
")" +
".flatten(1)" +
".mergeObjects()");
System.out.println(node.toPrettyString());
Output
{
"Car" : "Toyota",
"Bike" : "Schwinn",
"Scooter" : "Razor",
"Data" : [ {
"A0" : "11",
"B0" : "2000"
}, {
"A1" : "12",
"B1" : "4000"
}, {
"A2" : "13",
"B2" : "22000"
} ]
}
I am requesting via OKHttpClient data from my api, and im trying with an getAll() to split up the JSON response into my objects i need.
Here is an example for my response i get:
[
{
"value":"data",
"id":5
},
{
"value":"data",
"id":6
},
{
"value":"data",
"id":7
},
{
"value":"data",
"id":8
},
{
"value":"data",
"id":9
},
{
"value":"value",
"id":10
}
]
I believe you can use GSON in Android, so kindly see the code below:
static class SimpleExample{
private String value;
private Integer id;
}
public static void main(String[] args){
String json = "[{\n"
+ " \"value\":\"data\",\n"
+ " \"id\":5\n"
+ " },\n"
+ " {\n"
+ " \"value\":\"data\",\n"
+ " \"id\":6\n"
+ " },\n"
+ " {\n"
+ " \"value\":\"data\",\n"
+ " \"id\":7\n"
+ " },\n"
+ " {\n"
+ " \"value\":\"data\",\n"
+ " \"id\":8\n"
+ " },\n"
+ " {\n"
+ " \"value\":\"data\",\n"
+ " \"id\":9\n"
+ " },\n"
+ " {\n"
+ " \"value\":\"value\",\n"
+ " \"id\":10\n"
+ " }\n"
+ "]";
final GsonBuilder gsonBuilder = new GsonBuilder();
final Gson gson = gsonBuilder.create();
SimpleExample[] allObjects = gson.fromJson(json, SimpleExample[].class);
System.out.println(allObjects.length);
}
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
I have this JSON which I transformed into a POJO consisting of 3 different classes.
{
"Id": "2a0dd1fc",
"name": "AABBCCDD",
"description": "test",
"active": true,
"Groups": [
{
"agentGroups": [
{
"Id": "AXSNqSWSILMPnVvB-Cdc"
}
],
"order": 1,
"duration": 0
},
{
"agentGroups": [
{
"Id": "AXZlGzTR4pYEiRgUOqOL"
}
],
"order": 2,
"duration": 60
}
]
}
-----------------------------------AgentGroup.java-----------------------------------
public class AgentGroup{
public String id;
//Getter & Setters
}
-----------------------------------Group.java-----------------------------------
public class Group{
** public List<AgentGroup> agentGroups;**
public int order;
public int duration;
//Getter & Setters
public void setAgentGroups(List<AgentGroup> agentGroups) {
this.agentGroups = agentGroups;
}
}
-----------------------------------DistributionGroup.java-----------------------------------
public class DistributionGroup{
public String id;
public String name;
public String description;
public boolean active;
**public List<Group> groups;**
//Getter & Setters
public void setGroups(List<Group> groups) {
this.groups = groups;
}
}
As you can see Group is a List and agentGroup is also a List.
I'm having difficulty populating/setting up the list values and piece them together and return the Group as a list.
I have tried to set it up like this but it does not seem to be working:
//function call
List<Group> groups=createDistributionGroup(teamIds,1)
Function:
public static Group createDistributionGroup(List<String> teamIds, int order) {
Group grp= new Group();
grp.setOrder(order);
grp.setDuration(60);
//Now How do I put AgentGroup in a list and return the group.
return grp;
}
Appreciate any help.
Your model represents your json, more or less. Here's a simple test that you can use to validate it.
String s = "{\n"
+ " \"id\": \"2a0dd1fc\",\n"
+ " \"name\": \"AABBCCDD\",\n"
+ " \"description\": \"test\",\n"
+ " \"active\": true,\n"
+ " \"groups\": [\n"
+ " {\n"
+ " \"agentGroups\": [\n"
+ " {\n"
+ " \"id\": \"AXSNqSWSILMPnVvB-Cdc\"\n"
+ " }\n"
+ " ],\n"
+ " \"order\": 1,\n"
+ " \"duration\": 0\n"
+ " },\n"
+ " {\n"
+ " \"agentGroups\": [\n"
+ " {\n"
+ " \"id\": \"AXZlGzTR4pYEiRgUOqOL\"\n"
+ " }\n"
+ " ],\n"
+ " \"order\": 2,\n"
+ " \"duration\": 60\n"
+ " }\n"
+ " ]\n"
+ " }";
DistributionGroup distributionGroup = new ObjectMapper().reader()
.readValue(s, DistributionGroup.class);
I did make a few changes to your json, I set the field Id to id, and Groups to groups, notice the case difference. If you cannot change the json, then you have to add an annotation to your model attributes to let them know that they don't conform to the bean spec. For instance, if you wish to preserve Id in your json, then add #JsonAlias("Id") to the id property on your class, so the field would look like
#JsonAlias("Id")
public String id;
To add AgentGroups to your Group in the createDistributionGroup method, it would look something like this:
AgentGroup ag1 = new AgentGroup();
ag1.setId("10");
AgentGroup ag2 = new AgentGroup();
ag1.setId("20");
grp.setAgentGroups(Arrays.asList(ag1, ag2));
String is as follows :
"{
account_number={
type=long
},
firstname={
type=text, fields={
keyword={
ignore_above=256, type=keyword
}
}
},
accountnumber={
type=long
},
address={
type=text, fields={
keyword={
ignore_above=256, type=keyword
}
}
},
gender={
type=text, fields={
keyword={
ignore_above=256, type=keyword
}
}
}
}"
I need to get only the names of these fields, i.e, account_number,firstname,accountnumber,address,gender. Pojo class won't work here since the content inside objects is not fixed. A reg ex may work. Any suggestions ?
Here I have converted ur string into JSON and then retrieved all the keys
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONObject;
public class SOTest {
public static void main(String args[]) {
Set<String> keywords = new HashSet<String>();
final String regex = "[a-z]\\w*";
String string = "{\n"
+ " account_number={\n"
+ " type=long\n"
+ " },\n"
+ " firstname={\n"
+ " type=text, fields={\n"
+ " keyword={\n"
+ " ignore_above=256, type=keyword\n"
+ " }\n"
+ " }\n"
+ " },\n"
+ " accountnumber={\n"
+ " type=long\n"
+ " },\n"
+ " address={\n"
+ " type=text, fields={\n"
+ " keyword={\n"
+ " ignore_above=256, type=keyword\n"
+ " }\n"
+ " }\n"
+ " },\n"
+ " gender={\n"
+ " type=text, fields={\n"
+ " keyword={\n"
+ " ignore_above=256, type=keyword\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);
while(matcher.find()) {
String gp = matcher.group();
keywords.add(gp);
}
for (String keyword : keywords) {
string = string.replace(keyword, "\""+keyword+"\"");
}
string = string.replace("=", ":");
System.out.println(string);
JSONObject jsonObject = new JSONObject(string);
System.out.println(jsonObject.keySet());
}
}
output
[account_number, firstname, accountnumber, address, gender]
I want to convert my json string to string array. My JSON string is having two parameters "href" and "name". I want to create List of string of values of "name" parameter using java. I am using NetBeans for my application. please help me out to resolve this issue. I am getting error as
Exception in thread "AWT-EventQueue-0" org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]
JSONArray arr = new JSONArray(response);
List<String> list = new ArrayList<String>();
for(int i = 0; i < arr.length(); i++){
list.add(arr.getJSONObject(i).getString("name"));
System.out.println(arr.getJSONObject(i).getString("name"));
}
This is my JSON string
[
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/1",
"name": "analogValue_1"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/9",
"name": "analogValue_9"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/2",
"name": "analogValue_2"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/8",
"name": "analogValue_8"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/7",
"name": "analogValue_7"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/3",
"name": "analogValue_3"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/6",
"name": "analogValue_6"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/5",
"name": "analogValue_5"
},
{
"href": "\/api\/rest\/v1\/protocols\/bacnet\/local\/objects\/analog-value\/4",
"name": "analogValue_4"
}
]
Fix your json. 1. Change square brackets to curly brackets. 2. Each dictionary inside of your json is a value which must have a corresponding key. You code should look like this:
public static void main(String[] args) {
String myJSON = "{data_0:\n"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/1\",\n"
+ " \"name\": \"analogValue_1\"\n"
+ " },\n data_1:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/9\",\n"
+ " \"name\": \"analogValue_9\"\n"
+ " },\n data_2:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/2\",\n"
+ " \"name\": \"analogValue_2\"\n"
+ " },\n data_3:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/8\",\n"
+ " \"name\": \"analogValue_8\"\n"
+ " },\n data_4:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/7\",\n"
+ " \"name\": \"analogValue_7\"\n"
+ " },\n data_5:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/3\",\n"
+ " \"name\": \"analogValue_3\"\n"
+ " },\n data_6:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/6\",\n"
+ " \"name\": \"analogValue_6\"\n"
+ " },\n data_7:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/5\",\n"
+ " \"name\": \"analogValue_5\"\n"
+ " },\n data_8:"
+ " {\n"
+ " \"href\": \"\\/api\\/rest\\/v1\\/protocols\\/bacnet\\/local\\/objects\\/analog-value\\/4\",\n"
+ " \"name\": \"analogValue_4\"\n"
+ " }\n"
+ "}";
JSONObject jsonObject = new JSONObject(myJSON);
System.out.println("jsonObject: " + jsonObject.toString());
List<String> list = new ArrayList<String>();
System.out.println("jsonObject length: " + jsonObject.length());
for (int i = 0; i < jsonObject.length(); i++) {
list.add(jsonObject.getJSONObject("data_" + i).toString());
System.out.println(jsonObject.getJSONObject("data_" + i));
}
}
I added keys from data_0 to data_8. Then you create a list. Probably that does not exactly solve your problem, but least gives an idea where you're making a mistake.
From your comment we can see that you're using okhttp3.internal.http.RealResponseBody. Since the toString() method is not overwritten, the default implementation is used which is why System.out.println(response.body().toString()); prints okhttp3.internal.http.RealResponseBody#66cdc1bd.
To get the actual raw response use the string() method:
JSONArray arr = new JSONArray(responseBody.string());
According to the documentation (RealResponseBody extends ResponseBody):
String string()
Returns the response as a string decoded with the charset of the Content-Type header.
This also has already been discussed here by the way.