JSON Array key on an index value - java

I have two json response that are like so:
{
"test": [
{
"value": "abc"
}
]
}
Below is a different response
{
"test2": [
{
"value": "abc"
}
]
}
I have this line of code to grab an object from an array:
httpResponse.getBody()
.getObject()
.getJSONArray("test")
.getJSONObject(0)
.get("value")
.toString();
I have another one which is exactly the same but the DTD it's looking at is different:
httpResponse.getBody()
.getObject()
.getJSONArray("test2")
.getJSONObject(0)
.get("value")
.toString();
Instead of having two lines of code, one for each jsonArray key, I want a dynamic one where it simply selects the initial DTD value and then goes in and retrieve the value. How can this be done?

Can you not just make a method inside your class to help you with this?
String extractValue(HttpResponse httpResponse, String key) {
return httpResponse.getBody()
.getObject()
.getJSONArray(key)
.getJSONObject(0)
.get("value")
.toString();
}

Related

How to check if a value exists in a JSON Array for a particular key?

My JSON array file:
[
{
"setName": "set-1",
"testTagName": "Test1",
"methodName": "addCustomer"
},
{
"setName": "set-1",
"testTagName": "Test2",
"methodName": "addAccount"
},
{
"setName": "set-2",
"testTagName": "Test3",
"methodName": "addRole"
}
]
I use Java. I have the above JSON Array in a Gson object.
How do I iterate through this Gson array to check if a particular method name (eg: addRole), exists in the array for the key "methodName" in any of the objects of the JSON array? I am expecting true/false as a result.
I checked the GSON doc - (https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/JsonObject.java#L141)
The has method seems to check for the key. I am looking for a method that can iterate through the objects of the array and check if a particular value exists for a specific key.
How can I achieve this?
First you need to deserialize the JSON code to a JsonArray in this way:
JsonArray jsonArr = gson.fromJson(jsonString, JsonArray.class);
After that you can create this method:
public boolean hasValue(JsonArray json, String key, String value) {
for(int i = 0; i < json.size(); i++) { // iterate through the JsonArray
// first I get the 'i' JsonElement as a JsonObject, then I get the key as a string and I compare it with the value
if(json.get(i).getAsJsonObject().get(key).getAsString().equals(value)) return true;
}
return false;
}
Now you can call the method:
hasValue(jsonArr, "methodName", "addRole");
You can get the JSON in a JsonArray and then iterate over the elements while checking for the desired value.
One approach is suggested by #Crih.exe above. If you want to use Streams, you can convert the JsonArray into a stream and use anyMatch to return a boolean value
...
// Stream JsonArray
Stream<JsonElement> stream = StreamSupport.stream(array.spliterator(), true);
// Check for any matching element
boolean result = stream.anyMatch(e -> e.getAsJsonObject()
.get("methodName").getAsString().equals("addRole"));
System.out.println(result);
...

parsing a json which has attributes which might come as string or array of strings

I have a nested json where in the innermost array there are some keys for which the values could either be a string array or an array of array of strings. The json format is not consistent. How do I parse such a json using gson.
I have tried to write a custom de-serializer (see Gson - parsing json with field that is array or string) but that is throwing exception even before I could detect the attribute as string or array and then update the attribute accordingly.
my json is like this
{
"hits" : {
"total" : 100,
"max_score" : 1,
"hits": [
{"_index": "s1",
"_source":{
"activeOrExpired":[
["active"]
]
}
},
{"_index": "s1",
"_source":{
"activeOrExpired":[
"expired"
]
}
}
]
}
}
My java classes are
public class OuterJson {
#SerliazedName("hits")
public Hits hitsOuter;
public static class Hits {
public List<InnerHits> innerHits;
}
}
public InnerHits {
public String _index;
public Source _source;
public static class Source {
public List<List<String>> activeOrExpired;//I declare this field as
//list of list of strings
public Source() {
activeOrExpired = new ArrayList<>();
}
}
}
public class CustomDeserializer implements JsonDeserializer<OuterJson> {
#Override
public OuterJson deserialize(JsonElement elem, Type type, JsonDeserializationContext context) throws JsonParseException {
JsonObject outerObj = elem.getAsJsonObject();
JsonElement innerHits = outerObj.get("hits").getAsJsonObject().get("hits");
//I want to then detect the type of "activeOrExpired" and convert it
//to list of list of strings if it is present just as a string
//I am getting exception in the below line
InnerHits[] innerHitsArray = new Gson().fromJson(innerHits, InnerHits[].class);
//omitting below code for brevity since my code is failing above itself.
}
}
The exception is
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was String at path $[0]._source.activeOrExpired[0]
Here the innermost "hits" array has the "_source" array which has a field "activeOrExpired" this field is coming either as an array of Strings or array of array of strings.
How should I design the custom deserializer to handle such case?
I am new to gson and was following the method mentioned in the above link. My code is described above, could anyone please give me some hint on progressing. Thanks!
You can use DSM stream parsing library for such a complex JSON or XML. By using DSM you don't need to create java stub file to deserialize. you can directly deserialize to your own class.
It uses YAML based mapping file.
Here is the solution to your question. I am not sure about your object structure. I only deserialize some part of it.
Mapping File:
result:
type: object # result is map.
path: /hits
fields:
hits:
path: hits
type: array
fields:
index:
path: _index
source:
path: _source/activeOrExpired
filter: $value!=null
type: array # source is also array.
Use DSM to filter JSON and deserialize.
// you can pass your class to deserialize directly to your class instead of getting map or list as a result.
//DSM dsm=new DSMBuilder(new File("path/to/maping.yaml")).create(YourClass.class);
DSM dsm=new DSMBuilder(new File("path/to/maping.yaml")).create();
Map<String,Object> hits= (Map<String,Object>)dsm.toObject(new File("path/to/data.json");
json representation of hits variable
{
"innerHits" : [ {
"index" : "s1",
"source" : [ "active" ]
}, {
"index" : "s1",
"source" : [ "expired" ]
} ]
}

Print JSON with ordered properties

I have JSON with objects in specific order:
{
"Aaa": {
"Langs": {
"Val": [
"Test"
],
"Pro": [
"Test2"
]
}
},
"Bbb": {
"Langs": {
"Val": [
"Test"
],
"Pro": [
"Test2"
]
}
},
"Ddd": {
"Langs": {
"Val": [
"Test"
],
"Pro": [
]
}
},
}
And I would like to add new object Ccc between Bbb and Ddd. I tried to configure object mapper like this:
ObjectMapper mapper = new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
and then print with this code, but Ccc ends at the end of file.
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
//Write whole JSON in FILE
String finalJson = mapper.writer(prettyPrinter).writeValueAsString(rootFlores);
finalJson = finalJson.replaceAll("\\[ ]", "[" + System.lineSeparator() + " ]");
finalJson = finalJson.replaceAll("/", "\\\\/");
Files.write(Paths.get("DictionaryFlores_new.json"), Collections.singleton(finalJson));
Is here a way how to print JSON ordered?
Jackson deserialization/serialization does not sort properties
According to this answer, the Jackson SORT_PROPERTIES_ALPHABETICALLY only applies to POJO properties, not Maps. In JSON there is no difference between a Map and an Object, so you need to set the order in the Map first by using a LinkedHashMap or TreeMap
By definition, the keys of an object are unordered. I guess some libraries could offer an option to control the order of the keys when stringifying, but I wouldn't count on it.
When you need a certain order in json, you need to use an array. Of course, then you'd have to move the keys to a property in the child objects, and then the resulting array could only be indexed by number (not by the key). So then you might have to do additional processing to covert the data structure in the JSON to the data structure you really want to process.
Since you seems ready to use regex to update a JSON, I would suggest a "safer" approach. Don't try to create a pattern that would unsure that you don't update a value somewhere.
Iterate you values, on object at the time. Stringify the object and append the String yourself. That way, you are in charge of the object order. Example :
StringBuilder sb = new StringBuilder("{");
List<JsonPOJO> list = new ArrayList<>();
//populate the list
for(JsonPOJO pojo : list){
sb.append(pojo.stringify()).append(",");
}
sb.setLength(sb.length() - 1); //remove the last commma
sb.append("}");
Here, you are only managing the comma between each JSON object, not create the "complex" part about the JSON. And you are in full control of the order of the value in the String representation, it will only depend on the way you populate the List.
Note: sorry for the "draft" code, I don't really have access to my system here so just write this snippet to give you a basic idea on how to "manage" a JSON without having to recreating an API completely.
Note2: I would note suggest this unless this looks really necessary. As you mention in a comment, you are have only the problem with one key where you already have a JSON with 80000 keys, so I guess this is a "bad luck" scenario asking for last resort solution ;)

How to retrieve a value from an array inside of a JSON object using Jackson

Simply put, how do I retrieve {"value1":123"} using Jackson in a non-chaining way?
{
"aaa": [
{
"value1": "123"
}
],
"bbb": [
{
"value2": "456"
}
]
}
I tried using:
jsonNode.at("/aaa[Array][0]) but i get missing node in response.
Any help would be good.
The correct json path expression would be "/aaa/0/value1"
Use:
jsonNode.at("/aaa/0/value1")
use this below code :
JsonNode node = mapper.readTree(json);
System.out.println(node.path("aaa").get(0)); // {"value1":"123"}
use jackson-databind.
use this
node.path("aaa").get(0).get("value1") // 123.
Using node.path("aaa").get(0) is what retrieved the first item from array. Any other ideas like node.path("aaa[0]") or node.path("aaa/0") do not work.

change json elements' value in javax.json.JsonArray

[
{
"key":"key1",
"value":"key one value",
"description":""
},
{
"key":"key2",
"value":"key two value",
"description":""
},
{
"key":"key3",
"value":"key three value",
"description":""
},
{
"key":"key4",
"value":"key four value",
"description":""
},
{
"key":"key5",
"value":"key five value",
"description":""
}
]
This above is my an example json file I'm working with, I'm putting it into an JsonArray like this
BufferedReader reader = Files.newBufferedReader(file,
Charset.defaultCharset());
JsonReader jsonReader = Json.createReader(reader);
JsonArray array = jsonReader.readArray();
And My issue is I want to access the JsonArray and change the value part of each json element but is unable to do this.
the collection doesn't seem to offer anyways to replace values of any json element.
Do you know anyways I could achieve what I'm set to to do??
PS: also open to suggestions on using an alternative collection, but please educate me on why should I choose said collection.
Since you did not mention which JSON library you are using, you can use element method if you are using json-lib to replace an element
public JSONArray element(int index,
Object value)
If you want to update a specific attribute of the JSONObject element, you can try something like below
array.getJSONObject(0).put("key","new key value")
Please note that I have used hard coded value 0 for demonstration purposes.

Categories