I have been combing over multiple approaches with different JSON libraries, and cannot seem to find an elegant way to convert to and from with my JSON file in testing.
JSON file looks like this:
[
{
"LonelyParentKey": "Account",
"ProcessNames": [
{"Name": "ProcessOne",
"Sequence": "1"
},
{
"Name": "ProcessTwo",
"Sequence": "2"
},
{
"Name": "ProcessThree",
"Sequence": "3"
},
{
"Name": "ProcessFour",
"Sequence": "4"
}
]
}
]
In a QAF-based test using TestNG, am trying to import the values of the "ProcessName" key like this:
String lonelyParentKey = (String) data.get("LonelyParentKey");
ArrayList processNames = (ArrayList) data.get("ProcessNames");
I've seen that in the framework I'm using, I have multiple JSON library options, have been trying to use GSON after reading other SO posts.
So, next in the test code:
Gson gson = new Gson();
JSONArray jsa = new JSONArray(processNames);
What I am attempting to to create an object that contains 4 child objects in a data structure where I can access the Name and Sequence keys of each child.
In looking at my jsa object, it appears to have the structure I'm after, but how could I access the Sequence key of the first child object? In the REPL in IntelliJ IDEA, doing jsa.get(0) gives me "{"Name": "ProcessOne","Sequence": "1"}"
Seems like a situation where maps could be useful, but asking for help choosing the right data structure and suggestions on implementing.
TIA!
Not sure which library you're using, but they all offer pretty much the same methods. JSONArray looks like org.json.JSONArray, so that would be
JSONArray jsa = new JSONArray(processNames);
int sequenceFirstEntry = jsa.getJSONObject(0).getInt("Sequence");
Some JsonArray implementations also implement Iterable, then this also works
JSONArray jsa = new JSONArray(processNames);
for (JSONObject entry : jsa) {
int sequenceFirstEntry = entry.getInt("Sequence");
}
Any reason to not use DTO classes for your model?
e.g.
class Outer {
String lonelyParentKey;
List<Inner> processNames;
// getter/setter
}
and
class Inner {
String name;
String sequence;
// getter/setter
}
now your library should be able to deserialize your JSON string into a List. I have been using Jackson instead of GSON, but it should be similar in GSON:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
List<X> x = objectMapper.readValue(json, new TypeReference<List<X>>() {});
Related
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()"
I have a JSON which looks like this (number of fields heavily reduced for the sake of example):
{
"content": {
"id": {"content": "1"},
"param1": {"content": "A"},
"param2": {"content": "55"}
}
}
Keep in mind, that I don't have control over it, I can't change it, that is what I get from API.
I've created a POJO class for this looking like that:
public class PojoClass {
private String id;
private String param1;
private String param2;
// getters and setters
}
Then I parse JSON with Jackson (I have to use it, please don't suggest GSON or else):
ObjectMapper om = new ObjectMapper();
JsonNode jsonNode = om.readTree(json).get("content");
PojoClass table = om.readValue(jsonNode.toString(), PojoClass.class);
And this doesn't work, because of id, param1 and param2 having JSON in them, not straight values. The code works fine with JSON like this:
{
"content": {
"id": "1",
"param1": "A",
"param2": "55"
}
}
But unfortunately the values I need are stored under "content" fields.
What is the cleanest way to resolve this?
I understand that I can hardcode this and extract all values into variables one by one in constructor or something, but there are a lot of them, not just 3 like in this example and obviously this is not the correct way to do it.
You can modify the JsonNode elements like "id": {"content": "1"} to {"id": "1"} inside your json string accessing them as ObjectNode elements with an iterator and after deserialize the new json obtained {"id":"1","param1":"A","param2":"55"} like below:
String content = "content";
ObjectMapper om = new ObjectMapper();
JsonNode root = om.readTree(json).get(content);
Iterator<String> it = root.fieldNames();
while (it.hasNext()) {
String fieldName = it.next();
((ObjectNode)root).set(fieldName, root.get(fieldName).get(content));
}
PojoClass table = om.readValue(root.toString(), PojoClass.class);
System.out.println(table); //it will print PojoClass{id=1, param1=A, param2=55}
I've recently decided to rewrite one of my older android applications and I can't figure out how to convert server response like this:
{
"response": "SUCCESS",
"data": {
"0": {
... fields ...
},
"1": {
... fields ...
},
... another objects
}
}
to regular java object (or in this case list of objects). I was previously using this method:
JSONObject response = new JSONObject(stringResponse);
JSONObject dataList = response.getJSONObject("data");
int i = 0;
while (true) {
dataList.getJSONObject(String.valueOf(i)); // here I get wanted object
i++;
}
to get relevant objects and then I can put them into List, but now I'm using Retrofit library and I'm not able to find any clean solution to parse such weird object using gson and retrofit.
Thanks for any help.
Edit: What I want:
Send request using retrofit like this:
#GET("/some params")
void restCall(... another params..., Callback<Response> callback);
and then have List of objects in Response object. What I don't know is how to declare Response object, so it can convert that weird response into normal List of objects.
You have many libraries around for this.. One i used was json-simple There you can just use:
JSONValue.parse(String);
look into gson too! i'm using it for all my projects, serializing and deserializing to pojos is remarkably simple and customizable (if needed, most things are fine out of the box)
gson
here is their first example:
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
==> json is {"value1":1,"value2":"abc"}
obj = gson.fromJson( json );
==> you get back the same object
I have a map of JSON objects as follows:
Map<String,Object> map = HashMap<String,Object>();
map.put("first_name", "prod");
JSONObject jsonObj = new JSONObject("some complex json string here");
map.put("data", jsonObj);
Gson gson = new Gson();
String result = gson.toJson(map);
Now if the "some complex JSON string here" was:
{"sender":{"id":"test test"},"recipients":{"id":"test1 test1"} }
and execute above code gives me something like:
{
"first_name": "prod",
"data": {
"map": {
"sender": {
"map": {
"id": "test test"
}
}
},
"recipients": {
"map": {
"id": "test1 test1"
}
}
}
}
}
I might have some syntax error up there, but basically I don't know why I am seeing objects wrapped around map's.
Update
according to comments, it is a bad idea to mix different json parsers.
i can understand that. but my case requires calling an external api which takes a hash map of objects that are deserialized using gson eventually.
is there any other object bedsides JSONObject that i can add to the map and still have gson create json out of it without extra 'map' structure? i do understand that i can create java beans and achieve this. but i'm looking for a simpler way since my data structure can be complex.
Update2
going one step back, i am given a xml string. and i have converted them to json object.
now i have to use an external api that takes a map which in turn gets converted to json string using gson in external service.
so i am given an xml data structure, but i need to pass a map to that function. the way i have described above produces extra 'map' structures when converted to json string using gson. i do not have control to change how the external service behaves (e.g. using gson to convert the map).
Mixing classes from two different JSON libraries will end in nothing but tears. And that's your issue; JSONObject is not part of Gson. In addition, trying to mix Java data structures with a library's parse tree representations is also a bad idea; conceptually an object in JSON is a map.
If you're going to use Gson, either use all Java objects and let Gson convert them, or use the classes from Gson:
JsonObject root = new JsonObject();
root.addProperty("first_name", "prod");
JsonElement element = new JsonParser().parse(complexJsonString);
root.addProperty("data", element);
String json = new Gson().toJson(root);
This has to do with the internal implementation of JSONObject. The class itself has an instance field of type java.util.Map with the name map.
When you parse the String
{"sender":{"id":"test test"},"recipients":{"id":"test1 test1"} }
with JSONObject, you actually have 1 root JSONObject, two nested JSONObjects, one with name sender and one with name recipients.
The hierarchy is basically like so
JSONObject.map ->
"sender" ->
JSONObject.map ->
"id" -> "test test",
"recipients" ->
JSONObject.map ->
"id" -> "test test1"
Gson serializes your objects by mapping each field value to the field name.
Listen to this man.
And this one.
I'd a similar problem and I finally resolved it using json-simple.
HashMap<String, Object> object = new HashMap<String,Object>;
// Add some values ...
// And finally convert it
String objectStr = JSONValue.toJSONString(object);
You may try out the standard implementation of the Java API for JSON processing which is part of J2EE.
JsonObject obj = Json
.createObjectBuilder()
.add("first_name", "prod")
.add("data", Json.createObjectBuilder()
.add("sender", Json.createObjectBuilder().add("id", "test test"))
.add("recipients", Json.createObjectBuilder().add("id", "test1 test1"))).build();
Map<String, Object> prop = new HashMap<String, Object>() {
{
put(JsonGenerator.PRETTY_PRINTING, true);
}
};
JsonWriter writer = Json.createWriterFactory(prop).createWriter(System.out);
writer.writeObject(obj);
writer.close();
The output should be:
{
"first_name":"prod",
"data":{
"sender":{
"id":"test test"
},
"recipients":{
"id":"test1 test1"
}
}
}
I have data that looks like this
{
"status": "success",
"data": {
"data1": {
"serialNumber": "abc123",
"version": "1.6"
},
"data2": {
"irrelvent": [
"irrelvent",
"irrelvent"
]
},
"data3": {
"irrelevantLibs": {
"irrelevantFiles": [
"irrelevant.jar",
"irrelevant.jar",
"irrelevant.jar"
]
}
},
"data4": {
"configuration": "auth"
}
}
}
I am using the Jackson JSON Processor. What I need to do under the data object is to extract each data(x) into it's own data.
Hard to explain but I will try to be detailed. My intent is to make the JSON data more readable for our customers. So I'm trying to make the JSON data more friendly by splitting each data(x) object into blocks/tables on a website. So data1 will be displayed independently. Then data2 will be displayed independently and so on. So in this case I have four data(x) objects and I want to store those four objects into four different Strings and display them however I want. My problem is how do I get the Jackson Processor to do this?
Not sure if this is helpful but my current JSON function to process the JSON data is this:
public static String stringify(Object o) {
try {
ObjectMapper mapper = new ObjectMapper();
DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
printer.indentArraysWith(new Lf2SpacesIndenter());
return mapper.writer(printer).writeValueAsString(o);
} catch (Exception e) {
return null;
}
}
I'm positive that I can manipulate the processor to get those data separated but I don't know where to start.
EDIT: Hmm, since Jackson seems to be a pretty difficult parser to work with, would it be easier if I used the Javascript JSON parser to extract only the data objects? Since I already have Jackson convert the JSON data into a String, using Javascript would work?
"Hmm, since Jackson seems to be a pretty difficult parser to work with, would it be easier if I used the Javascript JSON parser to extract only the data objects?"
Seeing as you are working with Java have you considered using Sling? http://sling.apache.org/ It is an external library which will allow you parser and explore JSON data structures quite swiftly, and most of all cleanly.
So I was able to figure it out after really digging into the documentation. I had to use JsonNode in order to extract what I wanted. Note that the variable appName is just there for me to easily display the string data(x) while iteNode is just all elements under data(x)
public static List<String> jsonSplit(String o) throws JsonParseException, JsonMappingException, IOException {
List<String> list = new ArrayList<String>();
ObjectMapper mapper = new ObjectMapper();
DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
printer.indentArraysWith(new Lf2SpacesIndenter());
JsonNode rootNode = mapper.readValue(o, JsonNode.class);
Iterator<String> appName = rootNode.get("data").getFieldNames();
Iterator<JsonNode> iteNode = rootNode.get("data").getElements();
while (iteNode.hasNext()){
list.add(appName.next());
list.add(mapper.writer(printer).writeValueAsString(iteNode.next()));
}
return list;
}