I have to parse a JSON like this in Jackson:
"people": [
{
"personId": 1,
"name": "An",
"friends": [{"personId": 2}]
},
{
"personId": 2,
"name": "Bob",
"friends": [{"personId": 1}]
}
]
This should result in An's friends array containing Bob, and Bob's friends array containing An. I'm using this decorator on the Person class:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "personId")
The problem is that, during deserialisation, Jackson sets the first person in An's friends to null, as Bob hasn't been parsed yet. What's the best way to work around this?
If you have the possibility to slightly preprocess your JSON, so that the references are pure ints, the following approach would be the easiest for you:
#Data
#NoArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "personId")
public class People {
private int personId;
private String name;
private List<People> friends;
}
A little test for the deserialization:
public class ForwardReferenceTest {
#Test
void forwardReference() throws JsonProcessingException {
String json = "{\"people\": [\n" +
" {\n" +
" \"personId\": 1,\n" +
" \"name\": \"An\",\n" +
" \"friends\": [2]\n" +
" },\n" +
" {\n" +
" \"personId\": 2,\n" +
" \"name\": \"Bob\",\n" +
" \"friends\": [1]\n" +
" }\n" +
"]}";
Map<String, List<People>> people = new ObjectMapper().readValue(json, new TypeReference<Map<String, List<People>>>() {
});
assertThat(people.get("people").get(1).getFriends().get(0).getPersonId()).isEqualTo(1);
}
}
Could you give this a try?
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 have been using JsonPath. However after an issue yesterday where I discovered that the default JsonSmartJsonProvider didn't report an error with an invalid document at parse time, I modified my setup to use Jackson as below
public JsonPathExtractor(String document) throws DocumentFormatException
{
try
{
Configuration.setDefaults(new Configuration.Defaults()
{
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
#Override
public JsonProvider jsonProvider()
{
return jsonProvider;
}
#Override
public MappingProvider mappingProvider()
{
return mappingProvider;
}
#Override
public Set<Option> options()
{
return EnumSet.noneOf(Option.class);
}
});
// Get an object representation of the JSON to allow values to be extracted
this.document = Configuration.defaultConfiguration().jsonProvider().parse(document);
}
catch(Exception e)
{
throw new DocumentFormatException("Invalid JSON document", e);
}
}
However I see a difference in behaviour, in that if I get a path which has a few fields, they are not quoted, whereas they were when using JsonSmartJsonProvider.
Example JSON
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"height_cm": 167.6,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
],
"children": [],
"spouse": null
}
With the call:
Object obj = JsonPath.read(document, "$.phoneNumbers");
When using JacksonMappingProvider I get
[{type=home, number=212 555-1234}, {type=office, number=646 555-4567}]
When using JsonSmartJsonProvider I get:
[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"}]
If I want Jackson to behave the same way, is there something else that I can configure?
There's a difference between the way in which Jackson has handled the values and the way in which they are printed out.
When using JsonSmartJsonProvider this line ...
JsonPath.read(parse, "$.phoneNumbers");
... returns a JSONArray and the toString() method - which is called when you 'print' the JSONArray instance is smart enough to know it is dealing with JSON so it prints that state as a JSON string. For example:
[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"}]
But when you use a JacksonJsonProvider then this line ...
JsonPath.read(parse, "$.phoneNumbers");
... returns a List of LinkedHashMap and the toString() implementation invoked when you 'print' that instance is not JSON aware so it prints this:
[{type=home, number=212 555-1234}, {type=office, number=646 555-4567}]
If you want to print JSON when using the JacksonJsonProvider then you have to print it using something which is JSON aware. Here's an example:
String payload = "{\n" +
" \"firstName\": \"John\",\n" +
" \"lastName\": \"Smith\",\n" +
" \"isAlive\": true,\n" +
" \"age\": 25,\n" +
" \"height_cm\": 167.6,\n" +
" \"address\": {\n" +
" \"streetAddress\": \"21 2nd Street\",\n" +
" \"city\": \"New York\",\n" +
" \"state\": \"NY\",\n" +
" \"postalCode\": \"10021-3100\"\n" +
" },\n" +
" \"phoneNumbers\": [\n" +
" {\n" +
" \"type\": \"home\",\n" +
" \"number\": \"212 555-1234\"\n" +
" },\n" +
" {\n" +
" \"type\": \"office\",\n" +
" \"number\": \"646 555-4567\"\n" +
" }\n" +
" ],\n" +
" \"children\": [],\n" +
" \"spouse\": null\n" +
"}";
// this is a simpler way of declaring and using the JacksonJsonProvider
ObjectMapper objectMapper = new ObjectMapper();
Configuration conf = Configuration.builder()
.jsonProvider(new JacksonJsonProvider(objectMapper))
.build();
Object obj = JsonPath.using(conf).parse(payload).read("$.phoneNumbers");
// prints out:
// [{type=home, number=212 555-1234}, {type=office, number=646 555-4567}]
System.out.println(obj);
// prints out:
// [{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"}]
System.out.println(objectMapper.writer().writeValueAsString(obj));
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())
I need to read/write a file into the /src folder (I'm using Eclipse, it automatically generate src folder).
I have a dom object like this:
public class Person {
private String name;
private String surname;
... getters, setters and constructors here ...
}
in the /src folder, I need a "persons.json" file, where I want to add a new person, every time a new one is generated by a form (swing gui).
When I open the application, I want to load all the people in the file:
InputStream in = Person.class.getClassLoader().getResourceAsStream("scontrini_oggi.json");
String personString = IOUtils.toString(in, "UTF-8");
Person[] persons = gson.fromJson(personString, Person.class);
First problem: it gives an error with a file like this:
{
"name": "myname",
"surname": "my surname"
},
{
"name": "myname2",
"surname": "mysurname 2"
}
It prints the String personString (so it actually reads the file), but when it tries to convert to Person object, there is a ClassCastException.
Then, the other problem is that if I have a Person object, I want to append it to the file, but I have no idea how to achieve this.
Thank you!
First, the JSON example file you are providing is wrong.
This is not a valid JSON file:
{
"name": "myname",
"surname": "my surname"
},
{
"name": "myname2",
"surname": "mysurname 2"
}
Those are two objects separated by a comma. If you want a list of objects then the JSON file must be like this:
[
{
"name": "myname",
"surname": "my surname"
},
{
"name": "myname2",
"surname": "mysurname 2"
}
]
Then, as in the #Raman answer, you should ask for an array.
This a full working example.
package net.sargue.gson;
import com.google.gson.Gson;
import java.util.Arrays;
public class SO36629514 {
private class Person {
private String name;
private String surname;
#Override
public String toString() {
return "Person{" + "name='" + name + '\'' +
", surname='" + surname + '\'' +
'}';
}
}
public static void main(String[] args) {
String json = "[\n" +
" {\n" +
" \"name\": \"myname\",\n" +
" \"surname\": \"my surname\"\n" +
" },\n" +
" {\n" +
" \"name\": \"myname2\",\n" +
" \"surname\": \"mysurname 2\"\n" +
" }\n" +
"]";
Person[] persons = new Gson().fromJson(json, Person[].class);
System.out.println("persons = " + Arrays.toString(persons));
}
}
And this is the execution result:
persons = [Person{name='myname', surname='my surname'}, Person{name='myname2', surname='mysurname 2'}]
Shouldn't your code look like
Person[] myTypes = gson.fromJson(personString, Person[].class);
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());
}
}