DynamoDBMappingException: could not unconvert attribute, expected S in value - java

I am working on getting map type data stored in DynamoDB, the way I have tried so far:
use String getData() in my POJO, however, there is an issue that DynamoDBMappingException: could not unconvert attribute.
use Map<String, Object> getData() and annotated with #DynamoDBTypeConverted(converter = MapConverter.class) to retrieve the data, and also wrote a converter for it. This method also throws Exception DynamoDBMappingException: could not unconvert attribute, expected S in value.
So my question is:
is there a way to retrieve the standard JSON format data from DynamoDB by using mapper instead of DynamoJson without “N”, “S”, etc?
For instance:
"data": {
"cId":"777",
"cName":"NPR"
"isOwner":true
}
Once I get the standard JSON, I can use objectmapper to convert it to arbitrary data based on different use cases.
2. is it possible to use a map here? I found some folks create POJO for nested data, however, in my use case, the data fields vary from the different scenarios.
The converter I wrote:
public class MapConverter implements DynamoDBTypeConverter<String, Map> {
private final ObjectMapper objectMapper;
public MapConverter() {
objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
}
#Override
public String convert(final Map map) {
if (map == null) {
return null;
}
try {
return objectMapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
log.error("Hit exception while trying to convert map. {}.", map, e);
return null;
}
}
#Override
public Map unconvert(String serializedMap) {
if (serializedMap == null) {
return null;
}
try {
return objectMapper.readValue(serializedMap, Map.class);
} catch (IOException e) {
log.error("Hit exception while trying to unconvert map. {}.", serializedMap, e);
return null;
}
}
}
The data in the DynamoDB is like this:
{
"data": {
"M": {
"cId": {
"S": "777"
},
"cName": {
"S": "NPR"
},
"isOwner": {
"BOOL": true
}
}
}
}
and it may also look like
"data": {
"M": {
"globalEntityId": {
"S": "234"
},
"providerMetadata": {
"L": [
{
"M": {
"proj.123": {
"M": {
"entityId": {
"S": "s:123"
}
}
},
"PA": {
"M": {
"entityId": {
"S": "p:123"
}
}
}
}
}
]
}
}
}

Related

Gson append object to json file

I am currently working on a todo web application using Spring Boot. The problem I'm facing is that whenever I add an item it is not stored in my json file. This is my current json file:
[
{
"id": 0,
"task": "some task"
},
{
"id": 1,
"task": "some other task"
},
{
"id": 2,
"task": "some different task"
}
]
When I add my todo item with the application, it gets added to my dataprovider;
TodoDataProvider.java:
public class TodoDataProvider {
private static TodoDataProvider instance;
private List<TodoItem> todoItems = new ArrayList<>();
public static TodoDataProvider getInstance() {
if (instance == null) {
instance = new TodoDataProvider();
}
//read data from json
Gson gson = new Gson();
try {
FileReader reader = new FileReader("src/main/java/com/example/servingwebcontent/todos.json");
TodoItem[] tempTodos = gson.fromJson(reader, TodoItem[].class);
for (TodoItem tempTodo : tempTodos) {
instance.addTodo(tempTodo);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return instance;
}
... getters and setters
After I try to add this todo my json file becomes empty. What is the best way to add this new json object to the existing json file using gson?
This is the code where I add this item:
TodoController.java
#Controller
public class TodoController {
TodoDataProvider dataProvider = TodoDataProvider.getInstance();
#GetMapping("/")
public String greeting(Model model) {
model.addAttribute("todos", dataProvider.getTodoItems());
return "todo";
}
#PostMapping("/")
public String addTodo(Model model, #RequestParam("todoTask") String todoTask) {
TodoItem todoItem = new TodoItem(4, todoTask);
dataProvider.addTodo(todoItem);
Gson gson = new Gson();
try {
// TODO: 14-7-2022 add to json instead of replace
gson.toJson(dataProvider.getTodoItems(), new FileWriter("src/main/java/com/example/servingwebcontent/todos.json"));
} catch (IOException e) {
System.out.println(e.getMessage());
}
model.addAttribute("todos", dataProvider.getTodoItems());
return "redirect:/";
}
}

Jackson ContextualDeserializer does not deserialize all fields

I'm implementing a custom jackson deserializer for one of my entities.
My entity is the following:
#Value
#JsonDeserialize
#AllArgsConstructor
public class TestModel {
private final FieldUpdate<UUID> field1Update;
private final FieldUpdate<UUID> field2Update;
private final FieldUpdate<String> field3Update;
public String toString() {
return "TestModel. Field1="+(field1Update != null ? field1Update.toString() : null)+
" Field2="+(field2Update != null ? field2Update.getClass().getName() : null) +
" Field3="+(field3Update != null ? field3Update.getClass().getName() : null);
}
}
My problem is that serialiation works as expected - the successfully serialized object is as follow:
{
"field1Update" : {
"type" : "update",
"value" : "f59c4ef9-52c4-4f3d-99e5-a33a13ae12f3"
},
"field2Update" : {
"type" : "keep"
},
"field3Update" : {
"type" : "reset"
}
}
=> which is correct.
(There are the 3 Types Update, Keep and Reset). Only update needs a value.
The problem is: When i deserialize this, only the first field (field1Update) gets deserialized. The other 2 fields (field2Update and field3Update) are null after deserialization completes.
My Deserializer is the following:
public class FieldUpdateDeserializer extends StdDeserializer implements ContextualDeserializer {
private JavaType contentType;
public FieldUpdateDeserializer(JavaType contentType) {
this(null,contentType);
}
public FieldUpdateDeserializer() {
this(null,null);
}
public FieldUpdateDeserializer(Class<?> vc, JavaType contentType) {
super(vc);
this.contentType = contentType;
}
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException {
JavaType t = property.getType();
JavaType boundType = t.getBindings().getBoundType(0);
return new FieldUpdateDeserializer(boundType);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
if(!"type".equals(jp.nextFieldName()) )throw new JsonParseException(jp,"'type' expected");
String typeVal = jp.nextTextValue();
if("update".equals(typeVal)) {
jp.nextValue(); //consume type.
try {
JsonDeserializer deser = ctx.findNonContextualValueDeserializer(contentType);
return new Update<>(deser.deserialize(jp,ctx));
} catch (Exception ex) {
throw new IllegalStateException("Could not handle deserialization for type", ex);
}
} else if("keep".equals(typeVal)) {
return new Keep<>();
} else if("reset".equals(typeVal)) {
return new Reset<>();
} else {
return ctx.handleUnexpectedToken(FieldUpdate.class, jp);
}
}
}
An interesting fact is that jackson calls the deserialize(...) method only one time and i can't figure out why....
Glad if somebody can drop me a hint.
greetings,
Michael
Ok - after some sleep and analyzing what happens in the jackson serializer, i discovered that i did not consume enough tokens in my deserializer.
The working version for my deserializer is:
public Object deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
if(!"type".equals(jp.nextFieldName()) )throw new JsonParseException(jp,"'type' expected");
String typeVal = jp.nextTextValue();
if("update".equals(typeVal)) {
jp.nextValue(); //consume type.
try {
JsonDeserializer deser = ctx.findNonContextualValueDeserializer(contentType);
return new Update<>(deser.deserialize(jp,ctx));
} catch (Exception ex) {
throw new IllegalStateException("Could not handle deserialization for type", ex);
} finally {
jp.nextToken();
}
} else if("keep".equals(typeVal)) {
jp.nextToken();
return new Keep<>();
} else if("reset".equals(typeVal)) {
jp.nextToken();
return new Reset<>();
} else {
return ctx.handleUnexpectedToken(FieldUpdate.class, jp);
}
}

Better way to remove the json node at deeper level

The below is my sample json file which conatins the information about the cars, I wanted to remove the "city" from the below json data at a time.
Since the json data contains too many city nodes at very depath, I don't want to parse through each and every node to remove the "city" node. I want to remove the "city"
node in the easiest way without parsing the each node. I looked into string replace with empty but It is large data, It may fail in some cases.
Could any one tell me, what could be the better approach to remove the "city" node.
{
"carDetails":
[
{
"name":"John",
"city":"Berlin",
"cars":[
"audi",
"bmw",
"skoda": {
"model": "f3z2",
"manfactureDetails": {
"city": "vegas",
"time": "123967878734",
"color": "white",
"rawMaterial": {
"city": "london",
"quality": 1,
"importedDetails":{
"city" : "chile",
"date": "12/jan/2015",
...........
}
}
}
}
],
"job":"Teacher"
},
{
"name":"Mark",
"city":"Oslo",
"cars":[
"VW",
"Toyata" {
"manfactureDetails": {
"city" : "losangels",
.................
..................
}
}
],
"job":"Doctor"
}
]
}
Using jsonia, you can remove the field "on the fly":
public static void main(String[] args) {
try (InputStream stream = new FileInputStream("test.json")) {
try (OutputStream os = new FileOutputStream("output.json");
Writer writer = new OutputStreamWriter(os, "UTF-8");
PrintWriter out = new PrintWriter(writer)) {
JSonHandler formatter = new JSonFormatter(out, true);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
JSonHandler handler = (JSonHandler) Proxy.newProxyInstance(cl,
new Class<?>[] {JSonHandler.class},
new MyHandler(formatter));
JSonParser.parse(stream, "UTF-8", handler);
}
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
The MyHandler class looks like this:
private static class MyHandler implements InvocationHandler {
private int level = 0;
private final Object delegate;
public MyHandler(Object delegate) {
this.delegate = delegate;
}
#Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getName().equals("startField")
&& args[0].equals("city")) {
++level;
} else if (method.getName().equals("endField")
&& args[0].equals("city")) {
--level;
} else if (level == 0) {
return method.invoke(delegate, args);
}
return null;
}
}

GSON deserialize/serialize hierarchy class

I have a java app that send json content to my server (c++). I my server i receive the json, do the parse, the validation, etc and send back the response with json too.
I have one request in my java app that have this json body (example):
{
"a": "a",
"b": "b",
"searchMethod": {
"searchByUser": {
"userId": "userId"
}
}
}
But for the same command i can have other searchMethod:
{
"a": "a",
"b": "b",
"searchMethod": {
"searchByEmail": {
"email": "user#user.com"
}
}
}
So, when the user do the request we can send to my server one of this two different json bodys. I never know what searchMethod we send. This part (check what searchMethod the user send, etc), i do in my c++ server when i receive the json. So in my java app i only need to use the gson to send a searchMethod object with their content.
This my class to do the request:
public class RequestExample implements SerializableJSON
{
public String a;
public String b;
public RequestExample(String a, b)
{
this.a = a;
this.b = b;
}
public static RequestExample fromStringJson(String data)
{
try
{
Gson gson = new Gson();
return gson.fromJson(data, RequestExample.class);
}
catch(Exception e)
{
return null;
}
}
public static RequestExample fromBytesJson(byte[] data)
{
if (data == null) return null;
try
{
String str = new String(data, "utf-8");
return fromStringJson(str);
}
catch (Exception e)
{
return null;
}
}
#Override
public String toJsonString()
{
try
{
Gson gson = new Gson();
return gson.toJson(this);
}
catch(Exception e)
{
return null;
}
}
#Override
public byte[] toJsonBytes()
{
try
{
return this.toJsonString().getBytes("utf-8");
}
catch (Exception e)
{
return null;
}
}
}
I already implement the fields a and b because its always the same in this request. In this class the fromStringJson(String data) receive the data string field that contain all json that the user try to send. In this function i use the gson.fromJson to convert this string to a json object type of my RequestExample class.
So the main question is: How to adapt my RequestExample class to convert the string to a json object regardless of the type of searchMethod. Like i said in my java app i dont need to know how seachMethod the user choose. In my server yes, but this part i already implement. So for now, i only need to send the request to my server.
If you don't use field searchMethod, you can implement it like a Map
private Map<String, Object> searchMethod = new HashMap<>();
or
private Map<String, Map<String,Object>> searchMethod = new HashMap<>();
or
private Map<String, Map<String,String>> searchMethod = new HashMap<>();

how to parse this nested json response?

I am getting json response in this format.
{
"apiGroups":
{
"Affiliate":
{
"listingsAvailable":
{
"Beauty_Personal_Care":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:586:821655440?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Eyewear":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:473:662748456716?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Real_Estate":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:897:673143570606?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Jewellery":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:6:315773046?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Furniture":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:580:1894930153?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Tweens_Boys":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:814:934253466?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Automobiles":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:1145:639299259208?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Home_Improvement":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:864:624389489778?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"The_Designer_Studio":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:924:655684426383?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
"Fashion_Jewellery":
{
"listingVersions":
{
"v1":
{
"get": "http://affiliate-feeds.snapdeal.com/feed/api/category/v1:1113:672114192240?expiresAt=1446085800024&signature=civtucyhsbufsjzjvqfa"
}
}
},
I need to get categories like beauty personal care, eye ware and their respective urls in get field.How can i loop through this and get the.So far i tried like this and dont no how to proceed next.Can anybody give me suggestions how to parse this json?
json = jParser.getJSONFromUrl(response);
JSONObject api = json.getJSONObject("apiGroups");
JSONObject affiliate = api.getJSONObject("Affiliate");
JSONObject list = affiliate.getJSONObject("listingsAvailable");
You can read the documentation about the JSONObject class in Android.
In this documentation, you will find the method keys that will "Returns an iterator of the String names in this object."
So you just have to call this method and use the iterator.
Iterator<String> keysIterator = jsonObject.keys();
String key;
while (keysIterator.hasNext()) {
key = keysIterator.next();
//use the key to retrieve the data form jsonObject
}
However, if you are the one generating this json, you may consider changing it a bit. The data in the listingsAvailable should probably be in an array.

Categories