I'm working with a web service that enjoys making wheels. For a sequence of objects, instead of storing the data inside a JSON array, they make a new node for each index of the sequence.
{
"sequence": {
"0": {
"foo": "foo",
"bar": "bar",
"baz": "baz"
},
"1": {
"foo": "foo",
"bar": "bar",
"baz": "baz"
},
"2": {
"foo": "foo",
"bar": "bar",
"baz": "baz"
}
}
}
I was wondering if anyone had an elegant solution or sane approach to deserialize this into an array or collection of Sequence beans with jackson
public class SequenceElement {
String foo, bar, baz;
// Getters and setters below
}
Working with the sequence as a JsonNode is the best thing I can come up with atm, here is some untested sudo code.
ObjectMapper objectMapper = new ObjectMapper();
#JsonProperty("sequence")
public void setSequence(JsonNode sequence) {
List<SequenceElement> list = new ArrayList<SequenceElement>();
int i = 0;
while( sequence.get( String.valueOf(i) ) != null ) {
JsonNode element = sequence.get( String.valueOf(i) );
list.add( objectMapper.readValue( element, SequenceElement.class );
i += 1;
}
this.sequence = list;
}
IMO Map<String,Sequence> Should be Java equivalent for the JSON Object.
To extend on #SubirKumarSao's already correct answer, here is a way to get your sequence items as a list, in the same order as guaranteed by the data indices:
Data classes:
public class Sequence {
private String foo;
private String bar;
private String baz;
// Constructors, getters/setters
#Override
public String toString() {
return String.format("Sequence[foo=%s, bar=%s, baz=%s]", getFoo(),
getBar(), getBaz());
}
}
class SequenceHolder {
private Map<Integer, Sequence> sequence;
public SequenceHolder() {
sequence = new TreeMap<Integer, Sequence>();
}
// Other constructors, getters/setters
}
Main logic:
final String json = "JSON HERE";
final SequenceHolder holder = new ObjectMapper().readValue(json,
SequenceHolder.class);
System.out.println(holder.getSequence().values());
Tested with this modified version of your JSON (to illustrate ordering):
{
"sequence": {
"0": {
"foo": "foo0",
"bar": "bar0",
"baz": "baz0"
},
"1": {
"foo": "foo1",
"bar": "bar1",
"baz": "baz1"
},
"2": {
"foo": "foo2",
"bar": "bar2",
"baz": "baz2"
}
}
}
Output:
[Sequence[foo=foo0, bar=bar0, baz=baz0], Sequence[foo=foo1, bar=bar1, baz=baz1], Sequence[foo=foo2, bar=bar2, baz=baz2]]
As you can see, you get a list of items, in the same order as that guaranteed by your data indexes (the key being the use of a tree map).
Related
I'm quite new to API testing, I am wondering how to best and simple load some body?
I created simple pojo classes, but i am having problems with nested json.
ex:
{
"listOfItems": [
{
"name": "name1",
"value": "Jack"
},
{
"name": "nameDate",
"value": "20-08-2021-08-00-00"
},
{
"name": "address",
"value": "address here",
}
{
"name": "name2",
"value": "Smith"
}
],
"something": [],
"size": 1
}
Then, in classes I used:
ClassName {
private List<ListOfItems> listOfItems;
private List<something> something;
private int size;
//setters and getters
}
and
Class ListOfItems{
private String name;
private String value;
//getters and setters
}
then in test class I am trying to use it, but have no idea how.
public Class Test {
ClassName className = new ClassName();
ListOfItems list = new ListOfItems();
//how to get list with 3x name and 3x value like in json?
className.setsize(150);
given().when().body(???).post("\endpoint").then()...
}
But I have no idea how to declare those 4 properties (name, value)
You are actually on a pretty good track, you can use Gson library to help you out, Here is the video example for your explanation which I used to learn Gson back when I needed it
Consider the following JSON File:
{
"version": "1.0",
"firstData": {
"meta": "this is string",
"version": "1"
},
"SecondData": {
"meta": ["string1", "string2", "string3"],
"version": "1"
},
"ThirdData": {
"meta": true,
"version": "1"
},
"FourthData": {
"meta": [true, false, false, true],
"version": "1"
},
"FifthData": {
"meta": [{
"meta": "string",
"version": "2"
},
{
"meta": ["string1","string2"],
"version": "2"
}]
"version": "1"
}
}
As seen, The "meta" attribute has different data type, sometimes it is String, sometimes it is ArrayOfString, sometimes Boolean etc.
Since my JSON file has several data,
I want it to follow the following Structure :
class information
{
String version;
HashMap<String,Data> details;
}
class Data
{
variable meta;
String version;
}
How do I create a corresponding POJO and deserialize it using Google GSON?
Just define your meta as JsonElement. Then you will have sort methods like: getAsString, getAsBoolean, getAsJsonObject, getAsJsonArray, ..., and also you are able to deserialize it again after you find out what is the type.
So your class could look like:
public class SomeClass {
private int version;
private JsonElement meta;
//getters and setters and other stuff
}
Edit: More elaboration and implementation
Define two classes: GeneralItem and GeneralData
class GeneralItem
{
public final int version;
public final JsonElement meta;
}
class GeneralData
{
public final String version;
public final Map<String, GeneralItem> items;
public GeneralData(String version, Map<String, GeneralItem> items)
{
this.version = version;
this.items = items;
}
}
And then we define a custom deserializer for our GeneralData:
class GeneralDataDeserializer implements JsonDeserializer<GeneralData>
{
#Override
public GeneralData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
final JsonObject object = json.getAsJsonObject();
final String version = object.get("version").getAsString();
object.remove("version");
HashMap<String, GeneralItem> items = new HashMap<>(object.size());
for (Map.Entry<String, JsonElement> item : object.entrySet())
items.put(item.getKey(), context.deserialize(item.getValue(), GeneralItem.class));
return new GeneralData(version, items);
}
}
Finally registering the deserializer to our gson instance and getting the data:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(GeneralData.class, new GeneralDataDeserializer())
.create();
final String json = "your json here";
final GeneralData data = gson.fromJson(json, GeneralData.class);
System.out.println(data.items.get("firstData").meta.getAsString());
//other parts you want
(Note that constructors, getter and setters, error checking, etc. are removed for the sake of brevity)
Im using jsonProperty annonation to map json values to variables in pojo object.
{
"valueSet": [
{
"name": "Type_int",
"value": 123
},
{
"name": "Type_String",
"value": "ABC"
}
]
}
For this above json, I am using Object class object to capture "value" attribute value.
like
#JsonProperty("value")
private Object value;
This working fine to capture both "Integer" and "String" value.
But for below scenario
{
"valueSet": [
{
"name": "Type_int",
"value": 123
},
{
"name": "Type_String",
"value": "ABC"
},
{
"name": "Type_array",
"value": [
{
"x": 0,
"y": 0
},
{
"x": 10,
"y": 10
},
{
"x": 20,
"y": 20
}
]
}
]
}
There are three different data types for "value" attribute.I cannot use Object to capture value. So, is there any way to capture all values in "value" attribute.
Just trying my luck, with below code,
The Value class
public class Value {
#JsonProperty("name")
private String name;
#JsonProperty("value")
private Object value;
#JsonProperty("value")
private ValueObject valueObject;
//setters and getters
}
ValueSet class
public class ValueSet {
#JsonProperty("valueSet")
private List<Value> l;
//setters & getters
}
ValueObject class
public class ValueObject {
#JsonProperty("x")
private int x;
#JsonProperty("y")
private int y;
//setters & getters
}
And the test method
#Test
public void test() {
String input = "{\"valueSet\":[{\"name\":\"Type_int\",\"value\":123},{\"name\":\"Type_String\",\"value\":\"ABC\"},{\"name\":\"Type_array\",\"value\":[{\"x\":0,\"y\":0},{\"x\":10,\"y\":10},{\"x\":20,\"y\":20}]}]}";
//String input = "{\"valueSet\":[{\"name\":\"Type_int\",\"value\":123},{\"name\":\"Type_String\",\"value\":\"ABC\"}]}";
ObjectMapper mapper = new ObjectMapper();
try {
ValueSet v = mapper.readValue(input, ValueSet.class);
System.out.println(v);
} catch (IOException e) {
e.printStackTrace();
}
}
Output, in reverse order.
ValueSet [l=[Value [name=Type_int, value=123], Value [name=Type_String, value=ABC]]]
ValueSet [l=[Value [name=Type_int, value=123], Value [name=Type_String, value=ABC], Value [name=Type_array, value=[{x=0, y=0}, {x=10, y=10}, {x=20, y=20}]]]]
Add toString() methods to see output in log or console. And somehow this works for me. Not sure if it would work for you.
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("valueSet");
for(int i=0; i<jsonArray.length(); i++) {
System.out.println(jsonArray.optJSONObject(i).get("value").toString());
}
I have this function to return a valid JSON response:
public static Result response() {
ObjectNode result = Json.newObject();
result.put("status", "OK");
result.put("response", "Hello ");
return ok(result);
}
But what I want is to server an Array of objects in "result" property like:
{
"status": "OK",
"response": {
"results": [
{
"key1": "value",
"key2": 90,
"key3": "value"
},
{
"key1": "value"
"key2": 90,
"key3": "value",
}
]
}
}
How can I do this? I need to use Java and Play!
The Play framework uses Jackson. Therefore, you may use Jackson proper:
private static final JsonNodeFactory NODE_FACTORY = JsonNodeFactory.instance;
// ...
final ArrayNode results = NODE_FACTORY.arrayNode();
ObjectNode oneResult;
oneResult = NODE_FACTORY.objectNode(); // or Json.newObject();
oneResult.put(...); // etc
results.add(result);
//rinse, repeat for all other result objects, then:
result.put("results", results);
I guess the Json class also has .newArray() and such. Have a look at Jackson's ObjectNode, ArrayNode. Note: as far as I can remember, Play uses Jackson 1.9.x, which is prehistoric...
But really, you should try and use Jackson's {de,}serialization.
I did not wait for your answer about what is Play!, so I write based on Gson.
Try use this class:
public class Result implements Serializable {
String status;
Response response;
class Response implements Serializable {
List<ListItem> results;
class ListItem implements Serializable {
String key1;
Integer key2;
String key3;
}
}
}
It 100% works on your json fragment. You can supplement it with the necessary fields.
And then you can use Gson:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Result result = gson.fromJson(new FileReader(new File("json.json")), Result.class);
String json = gson.toJson(result);
System.out.println(json);
Prints:
{
"status": "OK",
"response": {
"results": [
{
"key1": "value",
"key2": 90,
"key3": "value"
},
{
"key1": "value",
"key2": 90,
"key3": "value"
}
]
}
}
Try in this direction.
How about returning an ArrayNode in the result, like:
public static Result foo() {
ArrayNode arrayNode = Json.newObject().putArray("bars");
arrayNode.add("hello");
arrayNode.add("world");
return ok(arrayNode);
}
I have the followed snipets of Json String:
{
"networks": {
"tech11": {
"id": "1",
"name": "IDEN"
},
"tech12": {
"id": "2",
"name": "EVDO_B"
}
}
}
I use some methods to convert this String to Object:
private static Gson mGson = new Gson();
...
public static WebObjectResponse convertJsonToObject(String jsonString) {
WebObjectResponse webObjectResponse = null;
if(jsonString != null && jsonString.length() > 1){
webObjectResponse = mGson.fromJson(jsonString, WebObjectResponse.class);
}
return webObjectResponse;
}
Where WebObjectResponse is class that should represent above mentioned String.
Its not complicated if I get static fields.
But in my case the values have different names: tech11, tech12 ....
I can use #SerializedName but its works in specific cases like convert "class" to "class_".
As you see networks Object defined as list of tech Objects but with different post-fix.
public class WebObjectResponse{
private DataInfoList networks = null;
}
This is static implementation, i defined 2 values tech11 and tech12 but next response might be techXX
public class DataInfoList {
private DataInfo tech11 = null;
private DataInfo tech12 = null;
}
public class DataInfo {
private String id = null;
private String name = null;
}
What is the good way to convert current Json String to Object where list of elements are Objects too and have different names?
Thank you.
Use a Map!
I would do the following
public class WebObjectResponse {
private Map<String, DataInfo> networks;
}
public class DataInfo {
private String id = null;
private String name = null;
}
// later
Gson gson = new Gson();
String json = "{\"networks\": {\"tech11\": { \"id\": \"1\",\"name\": \"IDEN\" }, \"tech12\": { \"id\": \"2\", \"name\": \"EVDO_B\" } }}";
WebObjectResponse response = gson.fromJson(json, WebObjectResponse .class);
For each object in json networks, a new entry will be added to the Map field of your class WebObjectResponse. You then reference them by techXX or iterate through the keyset.
Assuming a structure like this
{
"networks": {
"tech11": {
"id": "1",
"name": "IDEN"
},
"tech12": {
"id": "2",
"name": "EVDO_B"
},
"tech13": {
"id": "3",
"name": "WOHOO"
}, ...
}
}
We would need your class structure for more details.
As far as I am aware, I think you will need to have some mappings defined somewhere (I used xml's) and then try to match json with one of the mappings to create objects.
Google gson is good. I did it in Jackson
Also, converting objects should be trivial. But since you might have variable fields like tech11 and tech12 , you might want to store the "network" value as a string and then extract fields out of it when required.
Hope I could help.
Edit : Sotirious nails it.
Please use this link for converting SON Response to Java POJO class
http://www.jsonschema2pojo.org/