Querying JSON Object in Java - java

Suppose my JSON is :
{
"Name" : "Anmol Jain",
"Address" : [
{
"type" : "home",
"category" : "primary",
"street" : "ABC"
},
{
"type" : "home",
"category" : "secondary",
"street" : "XYZ"
},
{
"type" : "work",
"category" : "primary",
"street" : "PQR"
}
]
}
I am designing a specific syntax for my project so that I can query the JSON.
For Example :
Address(type = home; category = secondary).street
this should give me result as "XYZ".
The first approach that comes to my mind is to parse the above code syntax, and look for conditions like type and category (by string parsing).
then loop through Address array and try to match those conditions. But this will give higher time complexity in case of large JSONs.
Is there any library which does that. Or if anyone can suggest me better approach. The syntax for the condition is flexible and I can mould it accordingly.
Thanks in advance.

For example, use library "Josson & Jossons"
https://github.com/octomix/josson
implementation 'com.octomix.josson:josson:1.3.22'
------------------------------------------------
Josson josson = Josson.fromJsonString(
"{" +
" \"Name\" : \"Anmol Jain\"," +
" \"Address\" : [" +
" {" +
" \"type\" : \"home\"," +
" \"category\" : \"primary\"," +
" \"street\" : \"ABC\"" +
" }," +
" {" +
" \"type\" : \"home\"," +
" \"category\" : \"secondary\"," +
" \"street\" : \"XYZ\"" +
" }," +
" {" +
" \"type\" : \"work\"," +
" \"category\" : \"primary\"," +
" \"street\" : \"PQR\"" +
" }" +
" ]" +
"}");
String street = josson.getString("Address[type='home' & category='secondary'].street");

Besides #tgdavies mentioned, also have a look at https://github.com/eiiches/jackson-jq, it uses the same syntax of jq, but in java implementation.

Related

UpperCase all json keys using java and regex to find the matches

I'm beginner in java and have to replace all json Keys to upper Case .
My json can contain arrays (maybe nested Ones) and objects
Here is an example of my json input :
{
"transactionId": 181,
"Shipments": [
{
"shipmentId": 2,
"picklistId": "24RZ",
"ExtOrderId": "23-127",
"boxType": "120"
}
]
}
Here is an example of my json Output :
{
"TRANSACTIONID": 181,
"SHIPMENTS": [
{
"SHIPMENTID": 2,
"PICKLISTID": "24RZ",
"EXTORDERID": "23-127",
"BOXTYPE": "120"
}
]
}
I have tried to create an adapter but no chance to make it work correctly ,
Do i Have to use a regex and a replaceAll("everything before a : " , "upperCase()")
I'm reaching out to you guys to help me on this task because i could not find a way out to find a solution
One of the solutions is to flatten the JSON, upperCase() the keys and unflatten the JSON. Library Josson can do the transformation.
https://github.com/octomix/josson
Josson josson = Josson.fromJsonString(
"{" +
" \"transactionId\": 181," +
" \"Shipments\": [" +
" {" +
" \"shipmentId\": 2," +
" \"picklistId\": \"24RZ\"," +
" \"ExtOrderId\": \"23-127\"," +
" \"boxType\": \"120\"" +
" }" +
" ]" +
"}");
JsonNode node = josson.getNode(
"flatten('.').entries().map(key.upperCase()::value).mergeObjects().unflatten('.')");
System.out.println(node.toPrettyString());
Output
{
"TRANSACTIONID" : 181,
"SHIPMENTS" : [ {
"SHIPMENTID" : 2,
"PICKLISTID" : "24RZ",
"EXTORDERID" : "23-127",
"BOXTYPE" : "120"
} ]
}

Transform JSON and create new one

I'm working with JSON in Java, and I want to trasnform a JSON I have as entry :
{
"index":{
"mutlipleIndices":[
{
"index":"languages",
"values":[
"English",
"German"
]
},
{
"index":"editors",
"values":[
"AC. Renus",
"Lodiga"
]
}
],
"simpleIndices":[
{
"index":"bookName",
"values":"A song Of Ice and fire"
},
{
"index":"nbPages",
"valeurs":1600
}
]
}
}
int this :
{
"data": { ... Put here the entry JSON ... },
"flatData": [
{
"key": "languages",
"type": "string",
"key_type": "languages.string",
"value_string": ["English", "German"]
},
{
"key": "editors",
"type": "string",
"key_type": "editors.string",
"value_string": ["AC. Renus", "Lodiga"]
},
{
"key": "bookName",
"type": "string",
"key_type": "bookName.string",
"value_string": "A song Of Ice and fire"
},
{
"key": "nbPages",
"type": "float",
"key_type": "nbPages.float",
"value_float": 1600
}
]
}
My entry JSON is a JsonNode (Jackson library), how can I iterate on that object and create a new one ?
Basically what I thought I'd do is iternate on the entry json, find an entry and transform it
Thanks in advance
Create 2 pojos
Mapping the input json (let's call it MyJsonInput.java)
Mapping the output you want (let's call it MyOutputObject.java).
These 2 classes should have fields which matches the json structure.
Create a mapper:
ObjectMapper objectMapper = new ObjectMapper();
Read the json and translate it into your object:
MyJsonInput myInput = objectMapper.readValue(json, MyJsonInput.class); //where json is a string with the content of your json file.
Now you have a MyJsonInput instance (myInput) containing the json data and you can do your logic to flatten it and fill your MyOutputObject.
To get the json string of MyOutputObject instance use:
String outputAsString = objectMapper.writeValueAsString(myOutputObject);
You can also threat your both input and output as a generic Map<String,Object> but in my opinion not the best way because it can get very complicated reading and parsing maps containing other maps, and not to mention that you need to cast your data into the appropriate type (string, number etc..)
https://github.com/octomix/josson
https://mvnrepository.com/artifact/com.octomix.josson/josson
implementation 'com.octomix.josson:josson:1.3.21'
-------------------------------------------------
Jossons jossons = new Jossons();
jossons.putDataset("input", Josson.fromJsonString(
"{\n" +
" \"index\":{\n" +
" \"multipleIndices\":[\n" +
" {\n" +
" \"index\":\"languages\",\n" +
" \"values\":[\n" +
" \"English\",\n" +
" \"German\"\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"index\":\"editors\",\n" +
" \"values\":[\n" +
" \"AC. Renus\",\n" +
" \"Lodiga\"\n" +
" ]\n" +
" }\n" +
" ],\n" +
" \"simpleIndices\":[\n" +
" {\n" +
" \"index\":\"bookName\",\n" +
" \"values\":\"A song Of Ice and fire\"\n" +
" },\n" +
" {\n" +
" \"index\":\"nbPages\",\n" +
" \"values\":1600\n" +
" }\n" +
" ]\n" +
" }\n" +
"}"));
Map<String, String> dictionaryFinder = new HashMap<>();
// Define a dictionary function, $0 is the function parameter.
dictionaryFinder.put("flatIndices()",
"$0->map(" +
"key:index," +
"type:if(values.isArray(),values[0],values).if(isText(),'string','float')," +
"key_type:concat(index,'.',if(values.isArray(),values[0],values).if(isText(),'string','float'))," +
"value_string:values)");
// Define a left concatenate operation to combine two arrays.
dictionaryFinder.put("allIndices",
"flatIndices(input->index.multipleIndices) <+< flatIndices(input->index.simpleIndices)");
ResolverProgress progress = new ResolverProgress("\nProgress:");
// The query is a left concatenate operation to combine two objects.
JsonNode output = jossons.evaluateQueryWithResolver(
"input->toObject('data') <+< allIndices->toObject('flatData')",
dictionaryFinder::get, null, progress);
// Print result.
System.out.println(output == null ? null : output.toPrettyString());
// Print resolution progress.
System.out.println(String.join("\n", progress.getSteps()));
Output
{
"data" : {
"index" : {
"multipleIndices" : [ {
"index" : "languages",
"values" : [ "English", "German" ]
}, {
"index" : "editors",
"values" : [ "AC. Renus", "Lodiga" ]
} ],
"simpleIndices" : [ {
"index" : "bookName",
"values" : "A song Of Ice and fire"
}, {
"index" : "nbPages",
"values" : 1600
} ]
}
},
"flatData" : [ {
"key" : "languages",
"type" : "string",
"key_type" : "languages.string",
"value_string" : [ "English", "German" ]
}, {
"key" : "editors",
"type" : "string",
"key_type" : "editors.string",
"value_string" : [ "AC. Renus", "Lodiga" ]
}, {
"key" : "bookName",
"type" : "string",
"key_type" : "bookName.string",
"value_string" : "A song Of Ice and fire"
}, {
"key" : "nbPages",
"type" : "float",
"key_type" : "nbPages.float",
"value_string" : 1600
} ]
}
Progress:
Round 1 : Resolving allIndices from flatIndices(input->index.multipleIndices) <+< flatIndices(input->index.simpleIndices)
Round 1 : Resolving flatIndices(input->index.multipleIndices) from $0->map(key:index,type:if(values.isArray(),values[0],values).if(isText(),'string','float'),key_type:concat(index,'.',if(values.isArray(),values[0],values).if(isText(),'string','float')),value_string:values)
Round 1 : Resolved flatIndices(input->index.multipleIndices) = Array with 2 elements
Round 2 : Resolving flatIndices(input->index.simpleIndices) from $0->map(key:index,type:if(values.isArray(),values[0],values).if(isText(),'string','float'),key_type:concat(index,'.',if(values.isArray(),values[0],values).if(isText(),'string','float')),value_string:values)
Round 2 : Resolved flatIndices(input->index.simpleIndices) = Array with 2 elements
Round 3 : Resolved allIndices = Array with 4 elements
Round 4 : Resolved query result = Object with 2 elements

The mapping definition cannot be nested under a type [_doc] unless include_type_name is set to true

My API version is 6.3 and I try to create index. The failure says "The mapping definition cannot be nested under a type [_doc] unless include_type_name is set to true" .Here is my code:
CreateIndexRequest indexRequest = new CreateIndexRequest("user");
request.source("{\n" +
" \"settings\" : {\n" +
" \"number_of_shards\" : 1,\n" +
" \"number_of_replicas\" : 0\n" +
" },\n" +
" \"mappings\" : {\n" +
" \"_doc\" : {\n" +
" \"properties\" : {\n" +
" \"message\" : { \"type\" : \"text\" }\n" +
" }\n" +
" }\n" +
" },\n" +
" \"aliases\" : {\n" +
" \"twitter_alias\" : {}\n" +
" }\n" +
"}", XContentType.JSON);
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(indexRequest);
After I remove "_doc" as following, failure says "Failed to parse mapping [_doc]: No type specified for field [properties]". So what should I do now?
request.source("{\n" +
" \"settings\" : {\n" +
" \"number_of_shards\" : 1,\n" +
" \"number_of_replicas\" : 0\n" +
" },\n" +
" \"mappings\" : {\n" +
"
" \"properties\" : {\n" +
" \"message\" : { \"type\" : \"text\" }\n" +
" }\n" +
"
" },\n" +
" \"aliases\" : {\n" +
" \"twitter_alias\" : {}\n" +
" }\n" +
"}", XContentType.JSON);
_doc was a temporary type name which was kept for backward compatibility and to prepare for removing it, refer removal of type for more details on this.
Also refer schedule and how to handle this breaking change on your version
Indices created in 6.x only allow a single-type per index. Any name
can be used for the type, but there can be only one. The preferred
type name is _doc, so that index APIs have the same path as they
will have in 7.0: PUT {index}/_doc/{id} and POST {index}/_doc
Also please refer https://www.elastic.co/guide/en/elasticsearch/reference/7.x/removal-of-types.html#_migrating_multi_type_indices_to_single_type this section where its explained how to use it in 6.X.
I would suggest first try to create a index using REST API and see with the combination of include_type_name and index.mapping.single_type it works or not and accordingly add it in your code.
The reason was a mapping API query. I fixed as below.
# ES6
PUT myindex
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 2
},
"mappings": {
"_doc": { <--------- Delete this layer
"properties": {
"blahblah": {
# ES7
PUT myindex
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"blahblah": {

replace multi-line string in java

Trying to replace multi line string in java using replaceAll method but it's not working. Is there anything wrong with below logic?
String content=" \"get\" : {\n" +
" \"name\" : [ \"Test\" ],\n" +
" \"description\" : \"Test description to replace\",\n" +
" \"details\" : \"Test details\"";
String searchString=" \"name\" : [ \"Test\" ],\n" +
" \"description\" : \"Test description to replace\",";
String replaceString=" \"name\" : [ \"Actual\" ],\n" +
" \"description\" : \"Replaced description\",";
Tried below options and none of them worked-
Pattern.compile(searchString, Pattern.MULTILINE).matcher(content).replaceAll(replaceString);
Pattern.compile(searchString, Pattern.DOTALL).matcher(content).replaceAll(replaceString);
content = content.replaceAll(searchString, replaceString);
DISCLAIMER: You should not use regex to manipulate JSON or XML that have infinite nested content. Finite automate are not adapted to manipulate those data structures and you should use a JSON/XML parser instead.
This being said, purely for learning purpose, I will quickly fix your code.
1) Use either replace instead of replaceAll to avoid that your searchString is interpreted as a regex:
String content=" \"get\" : {\n" +
" \"name\" : [ \"Test\" ],\n" +
" \"description\" : \"Test description to replace\",\n" +
" \"details\" : \"Test details\"";
String searchString=" \"name\" : [ \"Test\" ],\n" +
" \"description\" : \"Test description to replace\",";
String replaceString=" \"name\" : [ \"Actual\" ],\n" +
" \"description\" : \"Replaced description\",";
System.out.println(content.replace(searchString, replaceString));
output:
"get" : {
"name" : [ "Actual" ],
"description" : "Replaced description",
"details" : "Test details"
2) Or use replaceAll but escape the brackets to avoid them being interpreted as a character class definition attempt.
String searchString=" \"name\" : \\[ \"Test\" \\],\n" +
" \"description\" : \"Test description to replace\",";
String replaceString=" \"name\" : [ \"Actual\" ],\n" +
" \"description\" : \"Replaced description\",";
System.out.println(content.replaceAll(searchString, replaceString));
output:
"get" : {
"name" : [ "Actual" ],
"description" : "Replaced description",
"details" : "Test details"
Link: How to parse JSON in Java
You should load your json structure in an object
Change the values of the attributes of that object to the new values
Export it again in json format

Compare JSON with nested arrays and jsons (Array order does not matter)

Hello I'm trying to compare two json in java, each key can contain a json object or array of json objects, and each one of them can also be an array or json.
Here is an example of the Json:
{
"id": "123123asd123",
"attributes": [
{
"name": "apps",
"values": [
"111",
"222"
]
},
{
"name": "city",
"values": [
"NY"
]
}
]
}
I want to be able to get two json from this kind and compare them without caring about the order of the arrays.
As you can see the key 'attributes is an array of json so if i have another json like the one up here and the element in the array with key city is before apps i want the test to pass.
aswell of the numbers inside the apps values i dont care if it is 111,222 or 222,111
If anyone know any external java lib that is doing that ill be happy hear.
Or any idea how to implement this compare manually? or even an idea that will this kind of json and reorganize it so it will be easy to compare share that with me.
Take a look at this library she is cool i'm using it all the day for my Webservice Test:
https://github.com/jayway/JsonPath
#Test
public void test() {
String json = "{\n" +
" \"id\": \"123123asd123\",\n" +
" \"attributes\": [\n" +
" {\n" +
" \"name\": \"apps\",\n" +
" \"values\": [\n" +
" \"111\",\n" +
" \"222\"\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"name\": \"city\",\n" +
" \"values\": [\n" +
" \"NY\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}";
List<String> names = JsonPath.read(json, "$.attributes[*].name");
for(String name : names) {
//TODO assert that name is in other list from other json
}

Categories