I have to read a JSON without knowing the keys.
My JSON is like this:
{
"hello": {
"bye": {
"a": "1",
"b": "2",
"c": "3"
},
"d": "4",
"e": "5"
},
"hey": {
"qwer": {
"asdf": "1",
"fda": "2",
"bc": "3"
},
"dq": "4"
}
}
just with more data and more elements.
I would like to access them like a map using a path, just with e.g.
get() for hello&hey,
get("hello") for bye,d&e,
get("hello","bye") for a,b&c
...
I hope you understood me, I usually use Gson to parse JSON but passing a corresponding class (like AClass c = new Gson.fromJson(myJson, AClass.class);), but without knowing the JSON structure, I cannot use this approach.
It would be also great if I could easily remove and add entries somewhere.
To do what you need, you can still use Gson, but you need a more "low level" class like the JsonParser.
Here I provide you an example on how you can navigate the JsonElement tree generated by the parser to reach the requested node. It works only on syntaxes like you requested (node1/node2/..) but you can easily adapt also to cases like this: node1/3/node3/4/2 where the number indicates the child.
When you reach the node, you can parse using a Gson if you need, since you can pass to Gson::fromJson a JsonElement also. Here's the code:
package stackoverflow.questions;
import java.util.*;
import com.google.gson.*;
public class Q20883087 {
public static JsonElement get(JsonElement je, String... args){
if(args.length == 0)
return je;
if (je != null && je.isJsonObject()){
String[] newArgs = Arrays.copyOfRange(args, 1, args.length);
return get(je.getAsJsonObject().get(args[0]), newArgs);
}
}
public static void main(String[] args) {
String json ="{ "+
" \"hello\": { "+
" \"bye\": { "+
" \"a\": \"1\", "+
" \"b\": \"2\", "+
" \"c\": \"3\" "+
" }, "+
" \"d\": \"4\", "+
" \"e\": \"5\" "+
" }, "+
" \"hey\": { "+
" \"qwer\": { "+
" \"asdf\": \"1\", "+
" \"fda\": \"2\", "+
" \"bc\": \"3\" "+
" }, "+
" \"dq\": \"4\" "+
" } "+
" } ";
JsonElement jsonElement = new JsonParser().parse(json);
System.out.println(get(jsonElement, "hello","bye"));
System.out.println(get(jsonElement, "hey","qwer"));
}
}
The result is:
{"a":"1","b":"2","c":"3"}
{"asdf":"1","fda":"2","bc":"3"}
Pay attention to the recursive nature of the get method, since Json is recursive by nature, the best way to manage it is using a recursive method.
Related
I am writing api tests. I am using rest assured to make the requests the following way:
public void POSTNewRequest(String endpoint, String requestBody){
response = given().auth().none()
.header("Content-Type", "application/json")
.contentType(ContentType.JSON)
.when()
.body(requestBody).log().all()
.post(endpoint);
}
The requestBody I'm passing in the request is constructed by converting custom java objects to a string using ObjectMapper.writeValueAsString(requestBody).
This method has been working great for me, but now I need to make a bunch of requests where there is a certain field missing. eg:
{
"foo": [
{
"description": "dflt desc",
"ref": "abcd",
"FIELDTOREMOVE": 0,
"customArray": {
"number": 22,
"letter": "B"
}
}
],
"moreInfo": {
"email": "test#test.be",
"name": "Jhon Doe"
}
}
Now I would like to remove the field "FIELDTOREMOVE" inside this request just before the post method. I tried to convert the requestBody string to a JsonNode and then removing the field but it doesn't remove the field.
private void removeNullFields(String requestBody) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(requestBody);
System.out.println(jsonNode.get("FIELDTOREMOVE"));
((ObjectNode)jsonNode).remove("FIELDTOREMOVE");
}
And when I try to print the value the field its returning "null" so I'm obviously doing something wrong...
I also tried achieving the same by using the gson library with similar results so I guess there is a misunderstanding on my end but can't figure out where to look to fix my problem.
In short: I'm making api requests using the rest assured library by passing a string as the body but in this string I sometimes have to remove certain fields the check what response I'm getting.
The "foo" in your JSON is an array. You should do:
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(requestBody);
jsonNode.get("foo").forEach(e -> ((ObjectNode) e).remove("FIELDTOREMOVE"));
System.out.println(jsonNode.toPrettyString());
Output:
{
"foo" : [ {
"description" : "dflt desc",
"ref" : "abcd",
"customArray" : {
"number" : 22,
"letter" : "B"
}
} ],
"moreInfo" : {
"email" : "test#test.be",
"name" : "Jhon Doe"
}
}
You can use this library:
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
So that the code would look like:
import org.json.JSONObject;
public class JSONManipulation {
static final String JSON = "{\n" +
" \"foo\": [\n" +
" {\n" +
" \"description\": \"dflt desc\",\n" +
" \"ref\": \"abcd\", \n" +
" \"FIELDTOREMOVE\": 0,\n" +
" \"customArray\": {\n" +
" \"number\": 22,\n" +
" \"letter\": \"B\"\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"moreInfo\": {\n" +
" \"email\": \"test#test.be\",\n" +
" \"name\": \"Jhon Doe\"\n" +
" }\n" +
"}";
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject(JSON);
System.out.println("Value before edit: " + jsonObject.toString());
jsonObject.getJSONArray("foo").getJSONObject(0).remove("FIELDTOREMOVE");
System.out.println("Value after edit: " + jsonObject.toString());
}
}
The output would be:
Value before edit: {"foo":[{"FIELDTOREMOVE":0,"ref":"abcd","description":"dflt desc","customArray":{"number":22,"letter":"B"}}],"moreInfo":{"name":"Jhon Doe","email":"test#test.be"}}
Value after edit: {"foo":[{"ref":"abcd","description":"dflt desc","customArray":{"number":22,"letter":"B"}}],"moreInfo":{"name":"Jhon Doe","email":"test#test.be"}}
So you can manipulate with your JSON content and then post is as a String.
I want to remove blob columns from JSON objects.
I need to check if any of the object has "#type": "blob", the entire column should be dropped.
Ex. following is a record from a DB. 'experience', 'hitpoints', 'name', 'uuid', 'image' (optional) are the columns. since the record has a blob column i.e image. It should be dropped.
Sample I/P:
{
"experience": 14248,
"hitpoints": 9223372036854775807,
"name": "Aaron1",
"uuid": "78edf902-7dd2-49a4-99b4-1c94ee286a33",
"image": {
"#type": "blob",
"content_type": "image/jpeg",
"digest": "sha1–4xlj1AKFgLdzcD7a1pVChrVTJIc=",
"length": 3888349
}
},
{
"experience": 14252,
"hitpoints": 92233720368512345,
"name": "Aaron2",
"uuid": "78edf902-7dd2-49a4-99b4-1a94ff286a45",
}
Sample O/P:
{
"experience": 14248,
"hitpoints": 9223372036854775807,
"name": "Aaron1",
"uuid": "78edf902-7dd2-49a4-99b4-1c94ee286a33",
},
{
"experience": 14252,
"hitpoints": 92233720368512345,
"name": "Aaron2",
"uuid": "78edf902-7dd2-49a4-99b4-1a94ff286a45",
}
Is there a way to achieve this by using optimized JSON parsing.
Currently, my logic follows the steps:
I'm parsing through the entire object using a function where I'm looping through the node to read the object.
Calling the 'blobChecker' function on every object.
Assigning null to the node if it contains blob.
Skipping the null node in the original function that invokes 'blobChecker'
The original function to parseJSON:
parseJsonNode(JsonNode node){
blobNodeChecker(node);
if(node!=null)
//The funtionality
}
The blobNodeChecker Function:
blobNodeChecker(JsonNode node) {
Boolean isBlob = false;
String blobNode = null;
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> next = fields.next();
String key = next.getKey();
String val = next.getValue().toString().toLowerCase();
if (key.equals("#type")) {
if (val.contains("blob")) {
isBlob = true;
break;
}
}
}
if (isBlob) {
node = null;
}
return node;
}
How about something like below. You can directly read a path and depending upon that delete a node. No need to loop all keys.
String tt = " {" +
" \"experience\": 14248," +
" \"hitpoints\": 9223372036854775807," +
" \"name\": \"Aaron1\"," +
" \"uuid\": \"78edf902-7dd2-49a4-99b4-1c94ee286a33\"," +
" \"image\": {" +
" \"#type\": \"blob\"," +
" \"content_type\": \"image/jpeg\"," +
" \"digest\": \"sha1–4xlj1AKFgLdzcD7a1pVChrVTJIc=\"," +
" \"length\": 3888349" +
" }" +
" }";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
JsonFactory factory = mapper.getFactory();
JsonParser createParser = factory.createParser(tt);
JsonNode actualObj1 = mapper.readTree(createParser);
JsonNode path = actualObj1.path("image").path("#type");
if( path != null && "blob".equalsIgnoreCase(path.asText())) {
((ObjectNode)actualObj1).remove("image");
}
System.out.println(actualObj1.toString());
I have a JSON file like this:
{
"waypoints": [
{
"waypoint_index": 0,
"trips_index": 0,
"hint": "u_FYj4uKI=",
"name": "",
"location": [
28.068655,
41.180774
]
},
{
"waypoint_index": 4,
"trips_index": 0,
"hint": "KiKhg4uKI=",
"name": "",
"location": [
20.75179,
29.031869
]
}
}
I know if you want to create java objects, well, you have just to understand how JSON works.
{} -> object
[] -> array
but I could not!
How to convert Java object to This json file?
public class ResultOsrm {
public Waypoints waypoints;
}
public class Waypoints {
public List waypoint_index;
}
Main -
Gson gson = new GsonBuilder().create();
ResultOsrm resultOsrm=gson.fromJson(JsonFile,ResultOsrm.class);
System.out.println(resultOsrm);
I need just waypoint_index and location values
I think ResultOsrm should hold list of Waypoint and class Waypoint will hold the data
public class ResultOsrm
{
public List<Waypoint> waypoints;
}
public class Waypoint
{
public int waypoint_index;
public int trips_index;
public String hint;
public String name;
public List<float> location;
}
waypoint_index is a variable in Waypoint, not a list by itself.
Use Kotlin to define the data classes.
WayPoint.kt
data class WayPoint(
#SerializedName("waypoint_index") var wayPointIndex: String,
#SerializedName("trips_index") var tripIndex: String,
#SerializedName("hint") var hint: String,
#SerializedName("name") var name: String,
#SerializedName("location") var location: ArrayList<String>
)
Response.kt
data class Response(
#SerializedName("waypoints") var wayPoints: ArrayList<WayPoint>
)
then to convert the string to JSON and to objects in Java class
String data = "{\n" +
" \"waypoints\": [\n" +
" {\n" +
" \"waypoint_index\": 0,\n" +
" \"trips_index\": 0,\n" +
" \"hint\": \"u_FYj4uKI=\",\n" +
" \"name\": \"\",\n" +
" \"location\": [\n" +
" 28.068655,\n" +
" 41.180774\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"waypoint_index\": 4,\n" +
" \"trips_index\": 0,\n" +
" \"hint\": \"KiKhg4uKI=\",\n" +
" \"name\": \"\",\n" +
" \"location\": [\n" +
" 20.75179,\n" +
" 29.031869\n" +
" ]\n" +
" }\n" +
"\t]\n" +
"}";
Response response = new Gson().fromJson(
data,
Response.class
);
The response class has all your data converted to the data class values.
Do try this solution. this will surely solve your issue.
Your JSON structure is little incorrect here -
{
"waypoints": [
{
"waypoint_index": 0,
"trips_index": 0,
"hint": "u_FYj4uKI=",
"name": "",
"location": [
28.068655,
41.180774
]
},
{
"waypoint_index": 4,
"trips_index": 0,
"hint": "KiKhg4uKI=",
"name": "",
"location": [
20.75179,
29.031869
]
}
]
}
You don't have closing array bracket for waypoints.
Also, you need to modify your class structure as per the JSON -
public class ResultOsrm {
private List<Waypoints> waypointsList;
// getter setter
}
public class Waypoints {
private Integer waypoint_index;
private List<Double> location;
// other fields & all getter setters
}
You should map every fields into objects & then use whatever you need out of it.
Note - Make your instance variable private & provide getters & setters
for fields. This is a good practice to have in POJO classes.
As the Sagar Nayak mentioned above is correct.
Kotlin
if you want to convert mockup data to ArrayList you could use this. It is the other options in case of you don't want to create a model, which contains ArrayList attribute within.
// when json variable is your mockup data
val list= Gson().fromJson<ArrayList<WayPoint>>(json, Array<WayPoint>::class.java).toCollection(ArrayList())
My json is
{
"name":"John",
"age":30,
"cars": [
{ "name":"Ford", "models":[ "Fiesta", "Focus", "Mustang" ] },
{ "name":"BMW", "models":[ "320", "X3", "X5" ] },
{ "name":"Fiat", "models":[ "500", "Panda" ] }
]
}
So i wrote sample junit test case for method carinfo which is
#Test
public void Test(){
assertEquals(carinfo.cars.get(0).name(), Temporary value);
}
I want a junit test case. so is it correct or any corrections
Assuming that your requirement is:
I have a routine which deserialises the given JSON into CarParts and Cars and when I test that routine I don't want to hardcode my expectations instead I'd like to be able to interrogate the JSON programmatically to form assertions
For example:
#Test
public void testJson() {
String json = "{\n"
+ " \"name\":\"John\",\n"
+ " \"age\":30,\n"
+ " \"cars\": [\n"
+ " { \"name\":\"Ford\", \"models\":[ \"Fiesta\", \"Focus\", \"Mustang\" ] },\n"
+ " { \"name\":\"BMW\", \"models\":[ \"320\", \"X3\", \"X5\" ] },\n"
+ " { \"name\":\"Fiat\", \"models\":[ \"500\", \"Panda\" ] }\n"
+ " ]\n"
+ " }";
CarInfo carinfo = deserialise(json);
// for the above JSON this JsonPath call will return "Ford"
Assert.assertEquals(JsonPath.read(json, "$.cars[0].name"), carinfo.getCars().get(0).getName());
}
If so, then JsonPath sounds like a good fit; it allows you to interrogate Json and to produce expectations for your test case like in the above example.
I have Json string, which contain "key" : "value" pairs and i need to know keys at this string using Java and importing org.json. I tried to use iterator, but first pair key prints in the end.
for example string:
{
"firstName": "sam",
"lastName": "Smith",
"address": {
"streetAddress": "somestreet",
"city": "somecity",
"postalCode": 101101
},
"phoneNumbers": [
"812 123-1234",
"916 123-4567"
]
}
I want to print:
"
firstName
lastName
address
phoneNumbers
".
But i have:
"lastName
address
phoneNumbers
firstName"
JSONObject JO = new JSONObject(JsonString);
Iterator<String> It = JO.keys();
while (It.hasNext()){
System.out.println(It.next());
}
From the JavaDoc:
A JSONObject is an unordered collection of name/value pairs.
If you want to order the keys you must 1) place them in a list and 2) order the list according your needs. Keep in mind that this is a key/value representation of data - the sequence of elements should not matter.
Using json-simple you can do this using the following code
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
public class TestJsonKeys {
public static void main(String[] args) throws ParseException {
final String jsonText = "{\n" + " \"firstName\": \"sam\",\n" + " \"lastName\": \"Smith\",\n"
+ " \"address\": {\n" + " \"streetAddress\": \"somestreet\",\n"
+ " \"city\": \"somecity\",\n" + " \"postalCode\": 101101\n" + " },\n"
+ " \"phoneNumbers\": [\n" + " \"812 123-1234\",\n" + " \"916 123-4567\"\n" + " ]\n"
+ "}";
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(jsonText);
System.out.println(json.keySet());
}
}