I want to deserialize the following JSON (in the original there are about 100 Exchanges with dynamic values):
{
"Exchange1": {
"EUR": [
"CNY",
"USD"
],
"INR": [
"USD",
"CNY"
]
},
"Exchange2": {
"BRL": [
"EUR",
"USD",
"INR"
],
"JPY": [
"USD",
"EUR",
"CNY"
]
},
....
}
I am using http://www.jsonschema2pojo.org/ but it is generating a Java class taking literal values ("Exchange1", "EUR", etc) while I need to iterate this no matter what is the dynamic String value:
How can I describe this JSON with Java?
You cannot have POJOs here (in sane sense), but people tend to use POJO-generators that do not do ahead analysis even for dynamic objects.
"Dynamic" objects should be typically mapped using java.util.Map (ordered implementation) + unique values can be mapped using java.util.Set (ordered implementation).
Thus, if you have a custom enumeration for the currencies, say something like
enum Currency {
BRL,
CNY,
EUR,
INR,
JPY,
USD,
}
then you can easily define the mapping without any POJOs that do not look applicable here at all:
private static final Type exchangesType = new TypeToken<Map<String, Map<Currency, Set<Currency>>>>() {
}.getType();
final Map<String, Map<Currency, Set<Currency>>> exchanges = gson.fromJson(jsonReader, exchangesType);
System.out.println(exchanges);
So the trivial toString() output will be as follows:
{Exchange1={EUR=[CNY, USD], INR=[USD, CNY]}, Exchange2={BRL=[EUR, USD, INR], JPY=[USD, EUR, CNY]}}
If you don't like the idea of having the Currency enumeration (you must always keep it up to date with recompilation, etc), then you can simply declare the currency markers as java.lang.String and get the same result:
private static final Type exchangesType = new TypeToken<Map<String, Map<String, Set<String>>>>() {
}.getType();
final Map<String, Map<String, Set<String>>> exchanges = gson.fromJson(jsonReader, exchangesType);
System.out.println(exchanges);
Related
As you can see in the example below, the first item in the attributes array is an object consisting of two string properties. The second object consists of a string and array property. I would like to decode both of these types of JSON objects into a collection of Java objects.
How I can express this in a POJO java class to handle decoding JSON like this?
attributes:[
{
"attribute_code": "has_options",
"value": "0"
},
{
"attribute_code": "ewc_top_quick",
"value": [
{
"label": "Display",
"value": "12.5",
"suffix": "''"
},
{
"label": "Grafica Integrata",
"value": "1",
"suffix": ""
}
]
}
]
So, you can use Map<String, Object> fro this field.
class Attr {
private String attribute_code;
private Map<String, Object> value;
}
After this, you can work with this object.
Also, you can use #JsonAnySetter. It's something similar to the previous option.
And the best way to resolve your situation it's custom deserializer. I strongly recommended this option.
I have class with field names:
private List<String> names;
Json that I'm getting looks like:
"names": [
{
"name": "a",
},
{
"name": "b",
},
{
"name": "c",
}
]
So I use custom serializing for transform this json to List:
#JsonProperty("names")
private void deserializeNames(List<Map<String, String>> json) {
names = json.stream().map(e -> e.get("name")).collect(Collectors.toList());
}
And this solution works, but when I will want to serialize objects of my class, new json will looks like:
"names": [
"a",
"b",
"c"
]
And my code for custom serializing won't work with it because it expects Map instead List
I tried to solve the problem using instanceof or deserialize list to map, but I think this is a wrong solution.
Is there any easy solutions for this problem?
You need not write custom Deserialiser for this,
You can define a class,
class Name {
#JsonProperty("name")
String name;
Name () {
}
}
You can use,
#JsonProperty("names")
private List<Name> names;
This will deserialise automatically. While serialising it will do the same.
I am reading the backup DynamoDB S3 bucket which has format for DynamoDB JSON.
I am trying to convert it into a normal JSON without the AttributeValue.
Original String
{
"id": {
"s": "someString"
},
"name": {
"b": "someByteBuffer"
},
"anotherId": {
"s": "someAnotherString"
},
"version": {
"n": "1"
}
}
Trying to convert to
{
"id": "someString",
"name": "someByteBuffer",
"anotherId": "someAnotherString",
"version": "1"
}
There are many answers which I referred to, but it doesn't convert into normal JSON, it gives me back the same JSON.
Here is what I tried:
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(inputJsonString);
final JacksonConverter converter = new JacksonConverterImpl();
Map<String, AttributeValue> map = converter.jsonObjectToMap(jsonNode);
Item item = ItemUtils.toItem(map);
Gson gson = new Gson();
System.out.println(gson.toJson(item.asMap()));
Also, when I was debugging, I wasn't able to create the Map properly. Map would contain key as "id", value as AttributeValue, but the AttributeValue would contain the string inside its own Map<String, AttributeValue> instead of inside String s
I feel I am missing something while creating the Map. Any pointers?
References on Stackoverflow:
Link 1
Link 2
What you're describing as the result sounds correct. If you look at the original JSON string, you have a list of key:value pairs where each value is another list of key:value pairs (length 1, but still a list).
To convert this to a single list of key:value pairs you need to write a map-reduce type of loop. Basically iterate the first map, take the key, then, from the second map (which is the value for the key you just recorded), just take the first entry's value (AttributeValue) and add that key:value pair into a new map (Map<String, AttributeValue>, which you define before the loop). That map you can then convert to JSON and it will result in the JSON string that you were expecting.
Hope that helps!
when using the javax json ObjectBuilder to build json, the output json string contains "value" and "num" in the json string. my web service is throw serialization error when the json contains "value" and "num". any one know why the output have "value" and "num"?
example:
JsonObject addProductRequest = Json.createObjectBuilder()
.add("OrderID", orderId)
.add("Product", productId)
.add("Quantity", qty)
.add("access_token", access_token)
.build();
output:
{
"OrderID": {
"num": 15498
},
"ProductID": {
"num": 20
},
"Quantity": {
"num": 1
},
"access_token": {
"value": "1f6c689d-6956-4f8e-b259-9030d57bec4b"
}
}
when I switch to using google.gson.JsonObject, the output dont have the "value" and "num" string, which my web service accepts and every thing seems to work fine.
example
com.google.gson.JsonObject addProductRequest = new com.google.gson.JsonObject();
addProductRequest.addProperty("OrderID", orderId);
addProductRequest.addProperty("Product", productId);
addProductRequest.addProperty("Quantity", qty);
addProductRequest.addProperty("access_token", access_token);
output:
{ "OrderID": 15499, "Product": 20, "Quantity": 1, "access_token": "3241cfd4-7b6c-4eac-b2bb-9b2b0c780831"}
Rest Assured seems to use Gson to serialize POJOs (which is what you should be using as response entities) to the response body.
Gson doesn't know anything about javax.json. The implementation of javax.json that you are using basically has this format for its JsonObject:
private static final class JsonObjectImpl extends AbstractMap<String, JsonValue> implements JsonObject {
private final Map<String, JsonValue> valueMap; // unmodifiable
Since this is a Map, Gson uses special serializing, iterating through its entrySet and using each Entry as a JSON key-value pair (within a JSON object).
In this case, the entrySet returns
#Override
public Set<Entry<String, JsonValue>> entrySet() {
return valueMap.entrySet();
}
where the valueMap contains all the values you added with add in your builder. For example, for
.add("OrderID", 1)
it will have added an entry with the String key OrderID and the JsonValue value of
// Optimized JsonNumber impl for int numbers.
private static final class JsonIntNumber extends JsonNumberImpl {
private final int num;
Notice the num field.
So Gson will now see a value of this JsonIntNumber type. It considers it a POJO and so serializes it as a JSON object with its fields as key-value pairs. It therefore produces
{
"num": 15498
}
com.google.gson.JsonObject is a known type to Gson. It knows that it is special and not a custom POJO type. It can therefore write it directly, instead of having to serialize it further.
This is similar to a question which I answered here:
Returning JsonObject using #ResponseBody in SpringMVC
Say I have a JSON file that looks like this:
{
"response" : [
{
"id" : "10",
"period" : "month",
"values" : [
{
"value" : 100,
"date" : "2013-05-10"
}
],
"parent" : "1"
},
{
"id" : "10",
"period" : "day",
"values" : [
{
"value" : {
"foo" : 10,
"bar" : 11,
"etc" : 4
},
"date" : "2013-05-10"
}
],
"parent" : "1"
},{
"id" : "13",
"period" : "year",
"values" : [
{
"value" : {
"info" : 1,
"pages" : 10,
"etc" : 4
},
"date" : "2013-05-10"
}
],
"parent" : "1"
}
]
}
Notice the 'values' part can either be a single value, or an object (which is unique).
I want to use the Jackson ObjectMapper to easily map this to a POJO.
What I have so far:
public class Response
{
List<ResponseEntry> response;
/*getters + setters */
public static class ResponseEntry
{
private String id;
private String period;
private String parent;
private List<Value> values;
/*setters + getters*/
public static class Value
{
private Object value;
private String date;
/*setters+getters*/
}
}
}
To map the response, I just specify the file I want and tell ObjectMapper to map to the 'Response' class
ObjectMapper mapper = new ObjectMapper();
Response r = mapper.readValues(json, Response.class);
This works, but is there a better way than just using 'Object' to hold 'value'? Since 'value' can be either a single value or an object, I'm having a bit of difficult figuring out what it should be. I'm certain there is a polymorphic way of handling this, but I've looked and couldn't find anything that worked. I'm pretty stuck and I would appreciate any help.
Unfortunately, with the structure of the JSON you are handling, the only way to deserialize it is to have the value attribute be type Object. However, once the JSON is deserialized, you can easily figure out whether value is an object or a single value.
Notice that JSON only supports five data types: objects (Map in java), arrays, strings, numeric and boolean. It looks like in your case, value would most likely be either a number or a map of numbers; then you have two possibilities to check for. Using a quick instanceof comparison, you should be able to figure out what type of value it is.
ObjectMapper mapper = new ObjectMapper();
Response r = mapper.readValues(json, Response.class);
Value val = r.response.get(0).values.get(0);
if (val.value instanceof Map)
; // multiple
else
; // single