Parsing JSON in Java and data manipulation - java

I'm super new to Java and am trying to parse JSON and manipulate the data into a final data structure as shown below.
I have this model:
public class StoreInfo {
private String storeName;
private String storeCode;
private List<StoreLocations> locations = new ArrayList<>();
}
Here is the JSON response I get from calling Redis:
redisResult = "[{
storeName: 'Walmart',
storeCode: '0001',
locations: [ [Object] ]
},
{
displayName: 'Wegmans',
storeCode: '0002',
locations: [ [Object] ]
}]"
When I call Redis, I use the keyName groceryStores and the fieldName 1.
I want to have a resulting data structure that looks like this:
groceryStores: { 1 : [
{
storeName: 'Walmart',
storeCode: '0001',
locations: [ [Object] ]
},
{
displayName: 'Wegmans',
storeCode: '0002',
locations: [ [Object] ]
}]
I'm having lots of trouble using Jackson to parse the initial JSON string into the type StoreInfo. When I try the following, I get an exception from Jackson:
StoreInfo[] storeInfoArray = objectMapper.readValue(redisResult, StoreInfo[].class);
I don't understand how I would create an array of StoreInfo objects.
Then, to use the key and field in order to create my final data structure.
I'm super new to Java and I have figured out how to do the latter part in Javascript, but how to do it in Java?
// Assuming the JSON response has been parsed
function addResponse (response, key, field, data) {
response[key] = {}
response[key][field] = data
return response
}

Easiest Option is create a wrapper class for StoreInfo i.e.
class StoreInfoDTO {
List<StoreInfo> storeInfoList;
//getter and setters
}
Now use ObjectMapper as:
StoreInfo[] storeInfoArray = objectMapper.readValue(redisResult,
StoreInfoDTO.class).getStoreInfoList();
Part2, setting the Value in response :
class ResponseDTO {
#JsonProperty("groceryStores")
Map<Integer, List<StoreInfo>> props;
//getter and setters
}
Now to use it :
ResponseDTO responseDTO = new ResponseDTO();
GrosseryStores grosseryStores = new GrosseryStores();
ArrayList<StoreInfo> storeInfos = new ArrayList<StoreInfo>();
storeInfos.add(new StoreInfo("SN1","1234"));
ArrayList<StoreInfo> storeInfos1 = new ArrayList<StoreInfo>();
storeInfos1.add(new StoreInfo("SN2","1236"));
Map<Integer, List<StoreInfo>> map = new HashMap<>();
map.put(1,storeInfos);
map.put(2,storeInfos1);
responseDTO.setProps(map);

Related

Create Java objects more directly from JSON with JsonPath

I create Objects from JsonPath with
List<String> httpList = JsonPath.read(json, "$.*.http_url_to_repo");
List<String> idList = JsonPath.read(json, "$.*.id");
for (int i = 0; i < httpList.size(); i++)
{
back.add(new GitlabProject(httpList.get(i), idList.get(i)));
}
Is there a more direct way to create the GitlabProject objects from the JSON?
The JSON looks like
[
{
"id": 63,
"description": null,
"name": "conti-maven-plugin-mm",
"name_with_namespace": "ik / spu / other / conti-maven-plugin-mm",
"path": "conti-maven-plugin-mm",
"path_with_namespace": "ik/spu/other/conti-maven-plugin-mm",
"created_at": "2023-01-30T12:33:59.218Z",
"default_branch": "main",
"tag_list": [],
"topics": [],
"ssh_url_to_repo": "git#gitlab-test.continentale.loc:ik/spu/other/conti-maven-plugin-mm.git",
"http_url_to_repo": "https://gitlab-test.continentale.loc/ik/spu/other/conti-maven-plugin-mm.git",
"web_url": "https://gitlab-test.continentale.loc/ik/spu/other/conti-maven-plugin-mm",
....
Assuming your POJO has different property names:
public class GitlabProject {
private int id;
#JsonProperty("http_url_to_repo")
private String httpUrlToRepo;
// Constructor, getters and setters omitted for brevity
}
Use ObjectMapper from the Jackson library to deserialize the JSON array into a list of GitlabProject objects:
ObjectMapper objectMapper = new ObjectMapper();
List<GitlabProject> projects = objectMapper.readValue(json, new TypeReference<List<GitlabProject>>(){});
You can do similar thing using Google's GSON library.
List<GitlabProject> projects = new Gson().fromJson(json, new TypeToken<List<GitlabProject>>(){}.getType());
Not that you'll need to use #SerializedName if property name is different in this case.
This can be done using JsonPath itself directly.
Just define a custom mapping function.
List<GitlabProject> projects = JsonPath.parse(json)
.read("$[*]", list -> list.stream()
.map(obj -> {
String httpUrl = JsonPath.read(obj, "$.http_url_to_repo");
int id = JsonPath.read(obj, "$.id");
return new GitlabProject(httpUrl, id);
})
.collect(Collectors.toList())
);
"$[*]" - selects all elements in the JSON array
The lamda expression inside "read" method extracts all
"http_url_to_repo" and "id" values from JSON and creates
corresponding "GitlabProject".
Finally the list of objects are created using
"Collectors.toList()"

How to create a JSON Array of JSON objects with a Java for loop?

I have a for loop which iterates and generates key value pairs for different employees.
I need to create a JSON array like below and write it to a JSON file at the end.
I am having trouble figuring out the ideal way to code it (JSON Objects -> JSON Array -> JSON file?).
I am open to use json-simple/GSON.
Desired JSON file format:
[
{
"employeeFirstName": "Mark",
"employeeLastName": "Williams",
"employeeDepartment": "Sales",
},
{
"employeeFirstName": "John",
"employeeLastName": "Carpenter",
"employeeDepartment": "Accounts",
},
{
"employeeFirstName": "David",
"employeeLastName": "Hunter",
"employeeDepartment": "Marketing",
},
]
I tried using a JSONObject and add it to a JSONArray. But, couldn't figure how to code it for iterations.
My current Java class:
public class Test {
public void createEmployeesJSONArrayFile(ITestContext iTestContext) {
for (ITestResult testResult : iTestContext.getFailedTests().getAllResults()) {
System.out.println("employeeFirstName: " + testResult.getEmployeeFirstName()));
System.out.println("employeeLastName: " + testResult.getEmployeeLastName());
System.out.println("employeeDepartment: " + testResult.getEmployeeDepartment());
}
}
}
What is the simplest or ideal way to achieve this?
A simple way to achieve this would be to use Gson, an API provided by Google. You could write the Collection of ITestResult objects to a file. The toJson function will take the Collection of ITestResult objects and write them to the the given Appenable object, which in this case is a BufferedWriter which points to a file.
(untested, one sec, not at workstation)
Collection<ITestResult> results = iTestContext.getFailedTests().getAllResults();
new GsonBuilder()
.create()
.toJson(results, Files.newBufferedWriter(Paths.get("path", "to", "file")));
If your goal is to write to file eventually, you can also use jackson apis.
ObjectMapper mapper = new ObjectMapper();
//To add indentation to output json
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Collection<ITestResult> results = iTestContext.getFailedTests().getAllResults();
try{
mapper.writeValue(new File("/somepath/output.json"), results);
catch (IOException){
e.printStackTrace();
}
Note: Recommended to use single instance of object mapper
For following snippet:
public static final class Node {
class Employee {
private final String employeeFirstName;
private final String employeeLastName;
private final String employeeDepartment;
public Employee(String employeeFirstName, String employeeLastName, String employeeDepartment) {
this.employeeFirstName = employeeFirstName;
this.employeeLastName = employeeLastName;
this.employeeDepartment = employeeDepartment;
}
}
List<Employee> employees = Arrays.asList(
new Employee("Mark", "Williams", "Sales"),
new Employee("John", "Carpenter", "Accounts"),
new Employee("David", "Hunter", "Marketing"));
// String json = ...
}
Using gson-utils
String json = GsonUtils.writeValue(data);
Using jackson-utils
String json = JacksonUtils.writeValue(data);

gson de-serialising object array without identifier

Im getting an error trying to take a json array to a list of objects, the exception is as below.
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 170
here is the code i am trying to use
public static void main(String[] args) throws FileNotFoundException {
Gson gson = new Gson();
Test test = new Test();
JsonElement json = gson.fromJson(test.getFile("fieldTypes.json"), JsonElement.class);
String result = gson.toJson(json);
System.out.println(result);
Type listType = new TypeToken<ArrayList<JiraField>>(){}.getType();
JiraField[] jiraFields = gson.fromJson(result, listType);
for (JiraField jiraField : jiraFields) {
System.out.println(jiraField);
}
}
This is the file contents
[
{
"id": "issuetype",
"key": "issuetype",
"name": "Issue Type",
"custom": false,
"orderable": true,
"navigable": true,
"searchable": true,
"clauseNames": [
"issuetype",
"type"
],
"schema": {
"type": "issuetype",
"system": "issuetype"
}
},
{
"id": "timespent",
"key": "timespent",
"name": "Time Spent",
"custom": false,
"orderable": false,
"navigable": true,
"searchable": false,
"clauseNames": [
"timespent"
],
"schema": {
"type": "number",
"system": "timespent"
}
}
]
The file is being read from the resources folder but that is working fine and the sysout is correctly showing the json contents. i assume there is something im doing wrong ?
ok, so. turns out to be caused by un mapped fields, this is how i got it working
in the object you add the #Expose annotation for the fields you want
public class JiraField {
#Expose
private String id ;
#Expose private String key ;
#Expose private String name ;
#Expose private boolean custom ;
#Expose private boolean orderable ;
#Expose private boolean navigable ;
#Expose private String[] clauseNames ;
Then when you initialise gson, you do it like this
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
then it works :)
JiraFields{id='issuetype', key='issuetype', name='Issue Type', custom=false, orderable=true, navigable=true, clauseNames=[issuetype, type]}
JiraFields{id='timespent', key='timespent', name='Time Spent', custom=false, orderable=false, navigable=true, clauseNames=[timespent]}
Try this:
Type listType = new TypeToken<ArrayList<JiraField[]>>(){}.getType();
I think we need to specify the JiraField[] in-order to get Array.
I'm assuming you have a minified JSON according to the exception message ...at line 1 column 170.... It would be nice if you provide both exact your exact JSON document and your mapping as well, since the posted formatted JSON now has the layout destroyed. I'm also assuming your mapping is custom and basically something like:
final class JiraField {
#SerializedName("id")
final String id = null;
#SerializedName("key")
final String key = null;
#SerializedName("name")
final String name = null;
#SerializedName("custom")
final boolean isCustom = Boolean.valueOf(false);
#SerializedName("orderable")
final boolean isOrderable = Boolean.valueOf(false);
#SerializedName("navigable")
final boolean isNavigable = Boolean.valueOf(false);
#SerializedName("searchable")
final boolean isSearchable = Boolean.valueOf(false);
#SerializedName("clauseNames")
final List<String> clauseNames = null;
#SerializedName("schema")
final List<String> schema = null;
}
Note the JiraField.schema field type: it's a list. Trying to deserialize a list of JiraFields with the default Gson configuration would result into:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 170 path $[0].schema
The symptom and the column are exactly the same (however, again, I'm just assyming your JSON file is minified), but note the path $[0].schema you probably missed to post: it says that an array begin token [ is expected (since the field is a List<String>), but was an object begin token {. Changing the field type to a more appropriate arbitrary Map<String, String> or any other custom appropriate POJO would fix it. Partially.
#SerializedName("schema")
final Map<String, String> schema = null;
Why partially? Your type token is bound to an ArrayList, but you're casting the deserialization result to an array JiraField[], therefore you'd get something like this:
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList cannot be cast to [LJiraField;
To fix it, you have to declare your jiraFields variable matching the deserialized value type: it's an array list, but not an array -- these are not the same in Java.
List<JiraField> jiraFields = ...
Another note regarding deserialization is that you don't need an intermediate json object to deserialize from. test.getFile most likely returns a java.io.Reader, so you can pass it directly to deserialize. Summarizing all up, the following should work for you:
// Immutable and thread-safe, can be instantiated once and shared
private static final Gson gson = new Gson();
// Immutable and thread-safe value type, the same story, + List instead of ArrayList - interfaces are usually much better
private static final Type listType = new TypeToken<List<JiraField>>() {
}.getType();
public static void main(final String... args)
throws IOException {
try ( final Reader reader = test.getFile("fieldTypes.json") ) {
final List<JiraField> jiraFields = gson.fromJson(reader, listType);
for ( final JiraField jiraField : jiraFields ) {
System.out.println(jiraField.key + " => " + jiraField.name);
}
}
}
Output:
issuetype => Issue Type
timespent => Time Spent
Type listType = new TypeToken<ArrayList<JiraField>>(){}.getType();
ArrayList<JiraField> jiraFields = gson.fromJson(result, listType);
for (JiraField jiraField : jiraFields) {
System.out.println(jiraField);
}
Try This code
Comment this line
Type listType = new TypeToken<ArrayList<JiraField>>(){}.getType();
Try with this
JiraField[] jiraFields = gson.fromJson(result, JiraField[].class);

Parsing JSON with java : dynamic keys

I need to create a JSON response with some dynamic fields in java. Here is an example of the JSON response I want to return :
{
"success": true,
"completed_at": 1400515821,
"<uuid>": {
type: "my_type",
...
},
"<uuid>": {
type: "my_type",
...
}
}
The "success" and the "completed_at" fields are easy to format. How can I format the fields? What would be the corresponding java object?
Basically I want to work with 2 java objects :
public class ApiResponseDTO {
private boolean success;
private DateTime completedAt;
...
}
and
public class AuthenticateResponseDTO extends ApiResponseDTO {
public List<ApplianceResponseDTO> uuids = new ArrayList<ApplianceResponseDTO>();
}
These java objects don't correspond to the expected JSON format. It would work if I could change the JSON format to have a list, but I can't change it.
Thanks a lot!
You can massage your data into JSON form using the javax.json library, specifically the JsonObjectBuilder and the JsonArrayBuilder. You'll probably want to nest a few levels of a toJson() method which will either give you the string representation you're looking for, or the JsonObject/JsonArray you desire. Something like this:
JsonArray value = null;
JsonArrayBuilder builder = Json.createArrayBuilder();
for (ApplianceResponseDTO apr : uuids) {
builder.add(apr.toJson());
}
value = builder.build();
return value;

converting string to gson object

I have a string output from server and I am trying to extract some values form the string.
Here is the output from server:
jsonString =
{
"MEANING":"reduce",
"DISPLAY":"",
"TYPE_CD":1,
"SELECTED_IND":1,
"CNT":1,
"SOURCES":[
{ "a":1 }
]
}
Code:
JsonReader reader = new JsonReader(new StringReader(jsonString));
DataObject obj1 = new Gson().fromJson(reader, DataObject.class);
DataObject Class:
DataObject
{
private int MEANING;
private int CNT;
private String TYPE_CD;
private String DISPLAY;
private String MEANING;
private List<Long> SOURCES;
public String getSourceTypeMeaning()
{
return this.MEANING;
}
public String getSourceTypeDisplay()
{
return this.DISPLAY;
}
public String getSourceTypeCd()
{
return this.TYPE_CD;
}
public int getSourceCount()
{
return this.CNT;
}
public List<Long> getSourceList()
{
return this.SOURCES;
}
}
but getting this error
Expected a string but was BEGIN_OBJECT at line 1 column 132
I am not able to find the issue with my code.
Other answers are pointing out that the problem is in the SOURCES field, and that's true, but the solutions they're giving are not correct...
You can't use just a Map to parse the SOURCES field, because this field is indeed an array! You have:
"SOURCES": [ ... ]
Since you have square brackets [ ], you have an array! And it's true there's a Map, but it is contained in the array...
So, what you need to parse that field correctly is:
private List<Map<String, int>> SOURCES;
Note that we use a Map to allow the content of SOURCES to have multiple and unknown values, so that this code could parse not only your JSON, but also something like:
"SOURCES":[
{ "a":1, "b":2 },
{ "c":3 },
{ "x":99, "y":98, "z":97 }
]
SOURCES variable should be Map<String,Long>,because in JSON string SOURCES is key-value collection ("a":1) where "a" is string and 1 is number.
Hope this helps.
Check this
"SOURCES":[
{ "a":1 }
]
This will represent a List of map not List of long.
So change your List<long> to List<Map<String, Long>> or List<Map<Object, Long>>.

Categories

Resources