I am getting json from dynamoDb that looks like this -
{
"id": "1234",
"payment": {
"payment_id": "2345",
"user_defined": {
"some_id": "3456"
}
}
}
My aim is to get the user_defined field in a Java HashMap<String, Object> as user_defined field can contain any user defined fields, which would be unknown until the data arrives. Everything works fine except my DynamoDBMapper cannot convert the user_defined field to a Java HashMap. It is throwing this error -
Exception occured Response[payment]; could not unconvert attribute
This is how the classes looks like -
#DynamoDBTable(tableName = "PaymentDetails")
public class Response {
private String id;
public Response() {
}
private Payment payment = new Payment();
#DynamoDBHashKey(attributeName="id")
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Payment getPayment() {
return payment;
}
public void setPayment(Payment payment) {
this.payment = payment;
}
}
The payment field mapper -
#DynamoDBDocument
public class Payment {
private String payment_id:
private HashMap<String, Object> user_defined;
public Payment() {}
public getPayment_id() {
return payment_id;
}
public setPayment_id(String payment_id) {
this.payment_id = payment_id;
}
#DynamoDBTypeConverted(converter = HashMapMarshaller.class)
public HashMap<String, Object> getUser_defined() {
return user_defined;
}
public void setUser_defined(HashMap<String, Object> user_defined) {
this.user_defined = user_defined;
}
}
The HashMapMarshaller(Just to check if Hashmap marshaller wasn't working with gson, I just defined a Hashmap, put in a value and return it, but seems to still not working) -
public class HashMapMarshaller implements DynamoDBTypeConverter<String, HashMap<String, Object>> {
#Override
public String convert(HashMap<String, Object> hashMap) {
return new Gson().toJson(hashMap);
}
#Override
public HashMap<String, Object> unconvert(String jsonString) {
System.out.println("jsonString received for unconverting is " + jsonString);
System.out.println("Unconverting attribute");
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("key", "value");
return hashMap;
//return new Gson().fromJson(jsonString, new TypeToken<HashMap<String, Object>>(){}.getType());
}
}
Marshaller approach is till now not working for me. It is also not printing any of the printlns I've put in there. I've also tried using #DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.M) and using Map instead of HashMap above my user_defined getter to no avail.
I want to find out how to convert the user_defined field to Java HashMap or Map. Any help is appreciated. Thank you!
Make Map<String, Object> to Map<String, String>. It should work without any custom converters. Otherwise be specific about Map's value type. For example, Map<String, SimplePojo> should work. Don't forget to annotate SimplePojo class with #DynamoDBDocument.
With Object as a type of Map's value, DynamoDB will not able to decide which object it has to create while reading entry from DynamoDB. It should know about specific type like String, Integer, SimplePojo etc.
Related
I have the following JSON
{
"ads": [
{
"228029_228029": {
"ad_id": "228029",
"duration": 10,
"m3u8_text": {
"_1280p": "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-ALLOW-CACHE:YES\n#EXT-X-TARGETDURATION:7\n#EXT-X-MEDIA-SEQUENCE:0\n#EXTINF:7.120000,\n_1280p_0000.ts\n#EXTINF:2.880000,\n_1280p_0001.ts\n#EXT-X-ENDLIST\n",
"_320p": "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-ALLOW-CACHE:YES\n#EXT-X-TARGETDURATION:7\n#EXT-X-MEDIA-SEQUENCE:0\n#EXTINF:7.120000,\n_320p_0000.ts\n#EXTINF:2.880000,\n_320p_0001.ts\n#EXT-X-ENDLIST\n"
}
}
},
{
"228845_228845": {
"ad_id": "228845",
"duration": 24,
"m3u8_text": {
"_1280p": "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-ALLOW-CACHE:YES\n#EXT-X-TARGETDURATION:8\n#EXT-X-MEDIA-SEQUENCE:0\n#EXTINF:7.840000,\n_1280p_0000.ts\n#EXTINF:6.880000,\n_1280p_0001.ts\n#EXTINF:6.680000,\n_1280p_0002.ts\n#EXTINF:2.600000,\n_1280p_0003.ts\n#EXT-X-ENDLIST\n",
"_320p": "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-ALLOW-CACHE:YES\n#EXT-X-TARGETDURATION:8\n#EXT-X-MEDIA-SEQUENCE:0\n#EXTINF:7.840000,\n_320p_0000.ts\n#EXTINF:6.880000,\n_320p_0001.ts\n#EXTINF:6.680000,\n_320p_0002.ts\n#EXTINF:2.600000,\n_320p_0003.ts\n#EXT-X-ENDLIST\n"
}
}
}
],
"total_duration": 80
}
I have created the respective model class as
This is the root model
#JsonIgnoreProperties(ignoreUnknown = true)
public class AdsResponse {
#JsonProperty("ads")
List<Ad> ads;
#JsonProperty("total_duration")
long totalDuration;
}
Then the ads model
public class Ad {
Map<String,AdInfo> ad;}
Then the AdInfo model
public class AdInfo {
#JsonProperty("m3u8_text")
AdManifest adManifest;
int duration;
#JsonProperty("ad_id")
String adId;}
Then the manifest model
public class AdManifest {
#JsonProperty("_1280p")
String _1280p;
#JsonProperty("_320p")
String _320p;}
When I try to parse this using below code
AdsResponse response = new ObjectMapper().readValue(
res,
AdsResponse.class);
I get the empy ad object
AdsResponse{ads=[Ad{ad=null}, Ad{ad=null}, totalDuration=80}
What is wrong here?
You don't actually need the Ad class, it's just a Map<>. AdResponse can look like this:
public class AdsResponse {
#JsonProperty("ads")
List<Map<String, AdInfo>> ads;
#JsonProperty("total_duration")
long totalDuration;
If the keys in the map were predictable, you could make them properties on the Ad class and then Jackson would map them properly. But since they're not (they look like some kind of ID), mapping them to a Map<> is probably the best option.
As an alternative, if you want or need to have the Ad objects, you can map them like this:
public class Ad {
Map<String, AdInfo> adInfo = new HashMap<>();
#JsonAnySetter
public void setAds(String key, AdInfo value) {
adInfo.put(key, value);
}
}
With that, and AdResponse defined the way you have it in the question, you'll get populated Ad instances, each of which has a Map<> with only 1 key/value pair. For an even simpler (and probably more sensible model, you can eliminate the Map if there is only ever 1 key in an Ad, like this:
public class Ad {
private AdInfo adInfo;
#JsonAnySetter
public void setAdInfo(String ignored, AdInfo value) {
this.adInfo = value;
}
}
I am trying to parse a json string to java object but i am not sure on the object hierarchy.
below is the json string
{
"TABLE_Detail":{
"1":{
"TABLE":"table1",
"RUN_DATE":"20170313",
"SEQ_NUM":"123",
"START_TIME":"20170313133144",
"END_TIME":"20170313133655"
},
"2":{
"TABLE":"table2",
"RUN_DATE":"20170313",
"SEQ_NUM":"123",
"START_TIME":"20170313133142",
"END_TIME":"20170313133723"
}
}
}
Here the number 1, 2 are dynamic and can go up to any number, I tried to create a outer object and have a Map of type key String and value as object TableData. The map having variable name TABLE_Detail. but the TableData object is always null. TableData object has all the variables.
Please help me on how to convert this json string to object.
Change 1 to table1 and 2 to table2:
public class TableDetails {
private TableData table1;
private TableData table2;
public TableDetails(){
}
// getter and setter
}
And if modify json format to "Koen Van Looveren" mentioned:
public class TableDetails {
List<TableData> tables;
public TableDetails() {
}
// getter and setter
}
The table class:
Table.java:
public class TableData {
private String table;
private String run_date;
private String seq_num;
private String start_time;
private String end_time;
public TableData() {
}
// getter and setter
}
you have two choice for such painfully json structure when using Gson.
using Gson parsing json as Map and write some class access returned Map.this mode works fine for access data only!
//usage
TableDetails details=new TableDetails(gson.fromJson(json, Map.class));
//classes
class TableDetails {
private Map<String, Map> context;
public TableDetails(Map root) {
this.context = (Map<String, Map>) root.get("TABLE_Detail");
}
public int size() {
return context.size();
}
public Table get(String key) {
return new Table(context.get(key));
}
}
class Table {
private Map context;
public Table(Map context) {
this.context = context;
}
public String getName() {
return get("TABLE");
}
private <T> T get(String name) {
return (T) context.get(name);
}
...
}
write your own Gson TypeAdapter,but this way may be more complex.if you interesting on write custom TypeAdapter there is a demo that I written for extract json root.gson-enclosing-plugin
You can try deserialize it into a Map<String, Map<String, TableData>>. The reason why Map<String, TableData> doesn't work it that the pesudo-array is wrapped in another object.
The following example converts a response into a List<TableData>:
public List<TableData> deserialize(String json) {
return Gson().<Map<String, Map<String, TableData>>>fromJson(json, new TypeToken<Map<String, Map<String, TableData>>>(){}.getType())
.values().iterator().next()
.entrySet().stream()
.sorted(Comparator.comparingInt(e -> Integer.parseInt(e.getKey())))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
}
I was in a search for the solution, and i came across one of the site where the solution worked. i wanted to credit the below site. Thanks for all the support.
I am able to map the dynamic value 1, 2 as map keys and values are mapped correspondingly to the TableData object properties using #SerializedName gson annootation.
http://findnerd.com/list/view/Parse-Json-Object-with-dynamic-keys-using-Gson-/24094/
When using an array in json you need to use [ for opening and ] for closing
{
"TABLE_Detail": [
{
"TABLE": "table1",
"RUN_DATE": "20170313",
"SEQ_NUM": "123",
"START_TIME": "20170313133144",
"END_TIME": "20170313133655"
},
{
"TABLE": "table2",
"RUN_DATE": "20170313",
"SEQ_NUM": "123",
"START_TIME": "20170313133142",
"END_TIME": "20170313133723"
}
]
}
My bean looks like this:
class MyBean {
private #JsonUnwrapped HashMap<String, String> map = new HashMap<String, String>();
private String name;
public HashMap<String, String> getMap() {
return map;
}
public void setMap(HashMap<String, String> map) {
this.map = map;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
While I'm serializing the bean using the following code:
MyBean bean = new MyBean();
HashMap<String, String> map = new HashMap<String, String>();;
map.put("key1", "value1");
map.put("key2", "value2");
bean.setMap(map);
bean.setName("suren");
ObjectMapper mapper = new ObjectMapper();
System.out.println("\n"+mapper.writeValueAsString(bean));
I'm getting result like this:
{"map":{"key2":"value2","key1":"value1"},"name":"suren"}
but
{"key2":"value2","key1":"value1","name":"suren"}
is expected per the JacksonFeatureUnwrapping documentation. Why am I not getting the unwrapped result?
#JsonUnwrapped doesn't work for maps, only for proper POJOs with getters and setters. For maps, You should use #JsonAnyGetter and #JsonAnySetter (available in jackson version >= 1.6).
In your case, try this:
#JsonAnySetter
public void add(String key, String value) {
map.put(key, value);
}
#JsonAnyGetter
public Map<String,String> getMap() {
return map;
}
That way, you can also directly add properties to the map, like add('abc','xyz') will add a new key abc to the map with value xyz.
There is an an open issue in the Jackson project to allow support for #JsonUnwrapped on maps.
Until this feature is supported, the workaround about using #JsonAnySetter/#JsonAnyGetter proposed in another answer appears to be the way to go, and is in fact a suggested approach by the Jackson project author in the open issue:
as per this:
http://www.cowtowncoder.com/blog/archives/2011/07/entry_458.html
one can use #JsonAnyGetter/setter to do something possibly similar. One missing pieces is that currently one must have getter (can't use it on Map filed), but that should be easy enough to address.
#JsonAnySetter
public void add(String key, String value) {
map.put(key, value);
}
#JsonAnyGetter
public Map<String,String> getMap() {
return map;
}
I am trying to parse a JSON object which consists of an Array of Customer objects. Each customer object contains a number of key/value pairs:
{
"Customers":
[
{
"customer.name": "acme corp",
"some_key": "value",
"other_key": "other_value",
"another_key": "another value"
},
{
"customer.name": "bluechip",
"different_key": "value",
"foo_key": "other_value",
"baa": "another value"
}
]
}
The complication is that the keys are not known to me in advance. A second complication is that the keys contain periods (.) that mean that even when I have tried to map them to a field, it fails.
I have been trying to map these to a Customers class:
Customers data = new Gson().fromJson(responseStr, Customers.class);
which looks like this:
public class Customers {
public List<Customer> Customers;
static public class Customer {
public List<KeyValuePair> properties;
public class KeyValuePair {
String key;
Object value;
}
}
}
My problem is that in when I load this class from the JSON, my Customers list populates, but their properties are null. How can I make GSON deal with the fact that I don't know the key names?
I have tried various other approaches including putting a HashMap in the Customer class, in place of the KeyValuePair class.
A different approach is that, you can create a Map of key values from the JSON and then look for the values, since the keys are not known
Gson gson = new Gson();
Type mapType = new TypeToken<Map<String,List<Map<String, String>>>>() {}.getType();
Map<String,List<Map<String, String>> >map = gson.fromJson(responseStr, mapType);
System.out.println(map);
Customers c = new Customers();
c.setCustomers(map.get("Customers"));
System.out.println(c.getCustomers());
Modify your Customers class like this
public class Customers {
public List<Map<String, String>> customers;
public List<Map<String, String>> getCustomers() {
return customers;
}
public void setCustomers(List<Map<String, String>> customers) {
this.customers = customers;
}
}
I have this code in my JSP page:
<h:selectManyCheckbox id="chb" value="#{MyBean.selectedCheckBoxes}" layout="pageDirection">
<f:selectItems value="#{MyBean.checkBoxItems}"/>
</h:selectManyCheckbox>
And in my MyBean:
public class MyBean {
public MyBean() {
for (Elem section : sections) {
checkBoxItems.put(section.getName(), section.getObjectID());
}
}
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
public String save() {
//save is not being executed....
return FORWARD;
}
public Map<String, Object> getCheckBoxItems() {
return checkBoxItems;
}
public void setCheckBoxItems(Map<String, Object> checkBoxItems) {
this.checkBoxItems = checkBoxItems;
}
public String[] getSelectedCheckBoxes() {
return selectedCheckBoxes;
}
public void setSelectedCheckBoxes(String[] selectedCheckBoxes) {
this.selectedCheckBoxes = selectedCheckBoxes;
}
}
When I click save it is giving the below message in <t:message for="chb"/>
"chb": Value is not a valid option.
Even though I did not add the required attribute for h:selectManyCheckbox, it is trying to validate or doing something else...
I've changed checkBoxItems variable type(with getter/setters) to List<SelectItem>, but it is not working as well.
What can be the reason, how can I solve it?
PS: I'm using JSF 1.1
You will get this error when the equals() test on a selected item has not returned true for any of the available items. So, when roughly the following happens under JSF's covers:
boolean valid = false;
for (Object availableItem : availableItems) {
if (selectedItem.equals(availableItem)) {
valid = true;
break;
}
}
if (!valid) {
// Validation error: Value is not valid!
}
That can in your particular case only mean that section.getObjectID() does not return a String which is what your selectedCheckboxes is declared to, but a different type or a custom type where equals() is not implemented or broken.
Update as per your comment, the getObjectID() returns Integer. It's thus been treated as String because selectedCheckBoxes is declared as String[]. You should change the following
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
to
private Integer[] selectedCheckBoxes;
private Map<String, Integer> checkBoxItems = new LinkedHashMap<String, Integer>();
and maybe (not sure, can't tell from top of head now) also explicitly supply a converter:
<h:selectManyCheckbox ... converter="javax.faces.Integer">
i didnt find any problem in th code, i thought there is the problem the list u passed to oneManyCheckBox.
hardcode some values in list in getter than check
public Map<String, Object> getCheckBoxItems() {
checkBoxItems.clear();
checkBoxItems.put("aaaa", "aaaa");
checkBoxItems.put("bbbb", "bbbb");
checkBoxItems.put("cccc", "cccc");
checkBoxItems.put("dddd", "dddd");
checkBoxItems.put("eeee", "eeee");
return checkBoxItems;
}