Trim redundant attributes in json objects using java - java

I would like to trim the below json object. That is a json object I built on top of what mongoDB responded. What I want to do is to remove just $oid because they are redundant attributes and keep the value inside (_id or $id ) without Curley braces and simply call the attribute id.
so what I need is just "id": "2283cef627ff2cc33ad5990"
Could you please help me I am struggling with json.
{
"_id": {
"$oid": "22383cef627ff2cc33ad5990"
},
"name": "data1",
"users": [
{
"$ref": "user",
"$id": {
"$oid": "16a5fbcee4b0c2c2da3017ef"
}
},
{
"$ref": "user",
"$id": {
"$oid": "1795ff86e4b09fc66416cd2f"
}
},
],
},

a) You can use a mapper to convert your JSON to an object and then call the desired attribute, like Jackson:
ObjectMapper mapper = new ObjectMapper();
String jsonInString = YOUR_STRING;
//from String to MyClass
MyClass object = mapper.readValue(jsonInString, MyClass.class);
In this example you have to define a class MyClass with all the attributes you need (e.g. _id, name, users, etc).
b) If you want to implement a quicker solution you can manipulate directly the string; if you know that the oid is always 24 characters you can do something like
String c = str.substring(str.indexOf("\"", str.indexOf("$oid")+6)+1, str.indexOf("\"", str.indexOf("$oid")+6)+25);
but I highly recommend to take a look to Jackson and give it a try. A solution like this is very fragile and every change in the JSON will lead to a wrong result.

Related

Jackson JSON Deserialization - How to assign object members based on JSON values?

I have some ugly JSON that I need to deserialize which looks like the following:
"ContainerValues": [
{
"ParentAttribute": "QuantityContained",
"RowList": [
{
"Values": [
{
"Name": "Code",
"ValuesByLocale": {
"en-US": "GRM"
},
},
{
"Name": "Value",
"ValuesByLocale": {
"en-US": "4.0"
},
}
],
}
],
}
],
This is just a sample of the JSON I have. All I need to do is to get this into a POJO which looks like something like the following:
Class POJO{
String grmValue; // This is the "Value" for the GRM "Code" above, i.e. "4.0"
...
}
Any idea how I might be able to assign the value of grmValue based on the JSON above using Jackson? I'm starting to think I'll need to write a custom deserializer.
First You have to deserialize to class similar to your JSON, then transform to your POJO format :)

Get json object in Array based on key and value in Java

I have a Json body like the example below. I need to extract the value from a key that has another key with a specific value in an array. I am passing in a JsonNode with everything in the detail component of the message, I can easily extract from each level, however, I'm struggling with the array.
In this case, I need to extract the value of "value" (Police/Fire/Accident Report) from the object in the array which has a key/value pair of "name":"documentTitle". I understand this is a JSONArray, but I can't find a good example that shows me how to extract the values for an object in the array that contains a certain key/value pair, I don't think I can rely on getting the object in position [2] in the array as the same objects may not always be present in the additionalMetadata array.
Sample Json:
"sourceVersion": "1.0",
"eventId": "8d74b892-810a-47c3-882b-6e641fd509eb",
"clientRequestId": "b84f3a7b-03cc-4848-a1e8-3519106c6fcb",
"detail": {
"stack": "corona",
"visibilityIndicator": null,
"documentUid": "b84f3a7b-03cc-4848-a1e8-3519106c6fcb",
"additionalMetadata": [
{
"name": "lastModifiedDate",
"value": "2021-05-21T04:53:53Z"
},
{
"name": "documentName",
"value": "Police/Fire Report, 23850413, 2021-05-20 14:51:23"
},
{
"name": "documentTitle",
"value": "Police/Fire/Accident Report"
},
{
"name": "documentAuthor",
"value": "System Generated"
},
{
"name": "lastModifiedBy",
"value": "System Updated"
},
{
"name": "createdBy",
"value": "System Generated"
},
{
"name": "documentDescription",
"value": "Police/Fire Report received"
},
{
"name": "organizationCode",
"value": "Claims"
}
]
}
}```
Loop through the json array and extract the json object with name documentTitile. From that json object you can get the value
Well, either the JSON framework you're using supports this out of the box (check the documentation) or you could convert it manually to a map:
List<AdditionalMetadataEntry> additionalMetadata;
[...]
Map<String, String> additionalMetadataMap = additionalMetadata.stream().collect(Collectors.toMap(AdditionalMetadataEntry::getName, AdditionalMetadataEntry::getValue));
I was able to figure it out. I created a new node off the existing notificationBody JsonNode, then parsed through the metadata key/value pairs:
String docTitle = "";
JsonNode additionalMetadata = notificationBody.get("detail").get("additionalMetadata");
for (JsonNode node: additionalMetadata) {
String name = node.get("name").asText();
String value = node.get("value").asText();
if(name.equals("documentTitle")){
docTitle = value;
}
}

How to select fields in different levels of a jsonfile with jsonPath?

I want to convert jsonobjcts into csv files. Wy (working) attempt so far is to load the json file as a JSONObject (from the googlecode.josn-simple library), then converting them with jsonPath into a string array which is then used to build the csv rows. However I am facing a problem with jsonPath. From the given example json...
{
"issues": [
{
"key": "abc",
"fields": {
"issuetype": {
"name": "Bug",
"id": "1",
"subtask": false
},
"priority": {
"name": "Major",
"id": "3"
},
"created": "2020-5-11",
"status": {
"name": "OPEN"
}
}
},
{
"key": "def",
"fields": {
"issuetype": {
"name": "Info",
"id": "5",
"subtask": false
},
"priority": {
"name": "Minor",
"id": "2"
},
"created": "2020-5-8",
"status": {
"name": "DONE"
}
}
}
]}
I want to select the following:
[
"abc",
"Bug",
"Major",
"2020-5-11",
"OPEN",
"def",
"Info",
"Minor",
"2020-5-8",
"DONE"
]
The csv should look like that:
abc,Bug,Major,2020-5-11,OPEN
def,Info,Minor,2020-5-8,DONE
I tried $.issues.[*].[key,fields] and I get
"abc",
{
"issuetype": {
"name": "Bug",
"id": "1",
"subtask": false
},
"priority": {
"name": "Major",
"id": "3"
},
"created": "2020-5-11",
"status": {
"name": "OPEN"
}
},
"def",
{
"issuetype": {
"name": "Info",
"id": "5",
"subtask": false
},
"priority": {
"name": "Minor",
"id": "2"
},
"created": "2020-5-8",
"status": {
"name": "DONE"
}
}
]
But when I want to select e.g. only "created" $.issues.[*].[key,fields.[created]
[
"2020-5-11",
"2020-5-8"
]
This is the result.
But I just do not get how to select "key" and e.g. "name" in the field issuetype.
How do I do that with jsonPath or is there a better way to filter a jsonfile and then convert it into a csv?
I recommend what I believe is a better way - which is to create a set of Java classes which represent the structure of your JSON data. When you read the JSON into these classes, you can manipulate the data using standard Java.
I also recommend a different JSON parser - in this case Jackson, but there are others. Why? Mainly, familiarity - see later on for more notes on that.
Starting with the end result: Assuming I have a class called Container which contains all the issues listed in the JSON file, I can then populate it with the following:
//import com.fasterxml.jackson.databind.ObjectMapper;
String jsonString = "{...}" // your JSON data as a string, for this demo.
ObjectMapper objectMapper = new ObjectMapper();
Container container = objectMapper.readValue(jsonString, Container.class);
Now I can print out all the issues in the CSV format you want as follows:
container.getIssues().forEach((issue) -> {
printCsvRow(issue);
});
Here, the printCsvRow() method looks like this:
private void printCsvRow(Issue issue) {
String key = issue.getKey();
Fields fields = issue.getFields();
String type = fields.getIssuetype().getName();
String priority = fields.getPriority().getName();
String created = fields.getCreated();
String status = fields.getStatus().getName();
System.out.println(String.join(",", key, type, priority, created, status));
}
In reality, I would use a CSV library to ensure records are formatted correctly - the above is just for illustration, to show how the JSON data can be accessed.
The following is printed:
abc,Bug,Major,2020-5-11,OPEN
def,Info,Minor,2020-5-8,DONE
And to filter only OPEN records, I can do something like this:
container.getIssues()
.stream()
.filter(issue -> issue.getFields().getStatus().getName().equals("OPEN"))
.forEach((issue) -> {
printCsvRow(issue);
});
The following is printed:
abc,Bug,Major,2020-5-11,OPEN
To enable Jackson, I use Maven with the following dependency:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
In case you don't use Maven, this gives me 3 JARs: jackson-databind, jackson-annotations, and jackson-core.
To create the nested Java classes I need (to mirror the structure of the JSON), I use a tool which generates them for me using your sample JSON.
In my case, I used this tool, but there are others.
I chose "Container" as the name of the root Java class; a source type of JSON; and selected Jackson 2.x annotations. I also requested getters and setters.
I added the generated classes (Fields, Issue, Issuetype, Priority, Status, and Container) to my project.
WARNING: The completeness of these Java classes is only as good as the sample JSON. But you can, of course, enhance these classes to more accurately reflect the actual JSON you need to handle.
The Jackson ObjectMapper takes care of loading the JSON into the class structure.
I chose to use Jackson instead of JsonPath, simply because of familiarity. JsonPath appears to have very similar object mapping capabilities - but I have never used those features of JsonPath.
Final note: You can use xpath style predicates in JsonPath to access individual data items and groups of items - as you describe in your question. But (in my experience) it is almost always worth the extra effort to create Java classes, if you want to process all your data in more flexible ways - especially if that involves transforming the JSON input into different output structures.

How to read a specific unique json attribute in jackson

This is a json of an api. my query only asks for one language (here "en") so there is only one value in the json. And this is the only thing i want to read in the json. So i think i doesnt make sense to convert it to an object. I thought of something like:
JsonNode root = mapper.readTree(genreJson); ...
But how do i get the value without knowing the name of the attribute (in the example "12345"). This is an Id i dont have.
What do you think?
{
"entities":
{
"12345":
{
"id": "12345",
"type": "item",
"descriptions":
{
"en":
{
"language": "en",
"value": "the_value_i_want"
}
}
}
},
"success": 1
}
i thought something like
JsonNode root = mapper.readTree(genreJson); ...
try this.. may work
JsonNode value = root.path("entities").path("12345").path("descriptions").path("en").path("value");
I am not sure if its an efficient approach though

Different JSON array response

I have problems parsing two different JSON responses.
1: This is the JSON response I get from a RESTful API:
{
"gear": [
{
"idGear": "1",
"name": "Nosilec za kolesa",
"year": "2005",
"price": "777.0"
}, {
"idGear": "2",
"name": "Stresni nosilci",
"year": "1983",
"price": "40.0"
}
]
}
2: This response I get from my testing client. I was added some values to the list and then I used gson.toJson for testing output.
[
{
"idGear": "1",
"name": "lala",
"year": 2000,
"price": 15.0
}, {
"idGear": "2",
"name": "lala2",
"year": 2000,
"price": 125.0
}
]
They are both valid, but the second one was successfully deserialize to object like this:
Type listType = new TypeToken<List<Gear>>() {}.getType();
List<Gear> gears= (List<Gear>) gson.fromJson(json, listType);
With the first one, I was trying to deserialize the same way but I get error.
EDIT
API Method:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Gear> getGear() {
List<Gear> gears = gearDAO.getGears();
if (!gears.isEmpty()) {
return gears;
} else
throw new RuntimeException("No gears");
}
CLIENT serialization code:
List<Gear> list = new ArrayList<Gear>();
Gear o = new Gear();
o.setPrice(15);
o.setYear(2000);
o.setName("asds");
Type listTypes = new TypeToken<List<Gear>>() {}.getType();
gson.toJson(list, listTypes);
The JSON responses are different!
The first one is an object, surrounded by { }, which contains a field "gear" that is in turn a list of objects, surrounded by [ ].
The second one is directly a list of objects, because it's surrounded by [ ]. Namely, the whole 2nd response is equivalent to the field in the 1st response.
So, obviously they can't be parsed in the same way...
The 2nd one is being parsed correctly because you are using a List and it is a list. But for the 1st one you need another class that contains a field that contains in turn a list... That is, you just need to create a class structure that represents your JSON responses...
public class Response {
private List<Gear> gears;
//getters & setters
}
Now you can parse your 1st response with:
Gson gson = new Gson();
Response response = gson.fromJson(json, Response .class);
List<Gear> gears = response.getGears();
I suggest you to take a brief look at json.org in order to understand JSON syntax, which is pretty simple...
Basically these are the possible JSON elements:
object
{}
{ members }
members
pair
pair , members
pair
string : value
array
[]
[ elements ]
elements
value
value , elements
value
string
number
object
array
true
false
null

Categories