I have nested Object (composition) use to represent data that i want to store and using Dyanmodb enhanced client as part of AWS Java version 2 api. In the readme it explains how to flatten the objects. In the version one of the api was able to store list of objects as json documents in dyanmodb.
public class Customer{
private String name;
private List<GenericRecord> recordMetadata;
//getters and setters for all attributes
}
public class GenericRecord {
private String id;
private String details;
//getters and setters for all attributes
}
Would like it to be stored as below not flattened for backward compatibility:
{
"name": "ABC",
"recordMetadata": [
{
"id":"123",
"details":"hello"
},
{
"id":"456",
"details":"yellow"
}
]
}
https://github.com/aws/aws-sdk-java-v2/blob/master/services-custom/dynamodb-enhanced/README.md
If I understood, you want to serialize the nested object to a String, just like the #DynamoDBTypeConvertedJson annotation did with the DynamoDBMapper in v1 of the AWS SDK for Java. There is nothing that comes out of the box to do this in v2 of the AWS SDK for Java. You'll have to write your own converter by hand as shown below.
But there's really no benefit to serializing it as a String, so you may consider just storing it as a nested document. It shouldn't require any changes to the code you posted. Storing it as a document does have benefits like being able to update a single nested field. You never know when a requirement may come along that requires this, and again, I'm not aware of any downside to storing it as a document.
Note: I don't think #DynamoDbFlatten will work in your case because it doesn't make sense to flatten a list.
class GenericRecordListConverter implements AttributeConverter<List<GenericRecord>> {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static GenericRecordListConverter create() {
return new GenericRecordListConverter();
}
#Override
public AttributeValue transformFrom(List<GenericRecord> input) {
try {
return AttributeValue.builder().s(MAPPER.writeValueAsString(input)).build();
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public List<GenericRecord> transformTo(AttributeValue input) {
try {
return MAPPER.readValue(input.s(), new TypeReference<List<GenericRecord>>() {});
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public EnhancedType<List<GenericRecord>> type() {
return EnhancedType.listOf(GenericRecord.class);
}
#Override
public AttributeValueType attributeValueType() {
return AttributeValueType.S;
}
}
This is resolved, don't need converter see:
.addAttribute(EnhancedType.listOf(EnhancedType.documentOf(GenericRecord.class,TableSchema.fromClass(GenericRecord.class))),
a -> a.name("recordMetadata").getter(Customer::getRecordMetadata)
.setter(Customer::setRecordMetadata) )
Official response:
https://github.com/aws/aws-sdk-java-v2/issues/2265
Related
I receive different objects set from the API. Each response have a follow structure:
items:[
{
user_id:1,
tags: {..}
},
{..}
]
The problem is that I do not want so unuseful and not readable structure.
I mean, all my methods (I use Retrofit library) must have some next signature:
Call<UserRepresantation>...
Call<RepoRepresentation>...
instead
Call<List<Users>>
Call<List<Repos>>
And also I have to use additional entities every time:
class UserRepresentation{
List<Users> items;
}
The Retrofite has possibility to use different converters for the serialization, for example:
Retrofit.Builder()
.baseUrl(stckUrl)
.addConverterFactory(GsonConverterFactory.create(new Gson())) < --- converter applying
.build();
As I understand I can use JsonSeializer to configure such behavior, but I can't figure out in which way. Can anyone help me to solve this issue?
So, in the simple words:
we have a response:
items:[
{
user_id:1,
tags: {..}
},
{..}
]
And we need to receive:
List<Users> = gson.fromJson(respose, User.class);
One solution would be to write a TypeAdapterFactory which performs the unwrapping when asked to deserialize any List<User> and List<Repo>, or in general for any List. However, the problem with this is that it would also apply to any nested lists of these types, for example when your User class has a field List<Repo> repos then that adapter factory would also try to unwrap its value, and fail.
So a more reliable solution might be to implement a TypeAdapterFactory which keeps track of whether it is currently being used to deserialize the top-level value and in that case unwrap / flatten the data. If not used for the top-level value it could simply let the other registered adapter factories handle the data:
class FlatteningTypeAdapterFactory implements TypeAdapterFactory {
public static final FlatteningTypeAdapterFactory INSTANCE = new FlatteningTypeAdapterFactory();
private FlatteningTypeAdapterFactory() { }
/** Tracks whether this is a nested call to this factory */
private static final ThreadLocal<Boolean> isNestedCall = new ThreadLocal<>();
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Only handle top-level value, for nested calls let other factory handle it
// Uses Boolean.TRUE.equals to handle case where value is `null`
if (Boolean.TRUE.equals(isNestedCall.get())) {
return null;
}
TypeAdapter<T> delegate;
isNestedCall.set(true);
try {
delegate = gson.getDelegateAdapter(this, type);
} finally {
isNestedCall.remove();
}
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) {
throw new UnsupportedOperationException();
}
#Override
public T read(JsonReader in) throws IOException {
in.beginObject();
String name = in.nextName();
if (!name.equals("items")) {
throw new IllegalArgumentException("Unexpected member name: " + name);
}
T value;
// While using delegate adapter also set isNestedCall in case delegate looks up
// another adapter dynamically while its `read` method is called
isNestedCall.set(true);
try {
value = delegate.read(in);
} finally {
isNestedCall.remove();
}
in.endObject();
return value;
}
};
}
}
You would then have to register it with a GsonBuilder before constructing the GsonConverterFactory:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(FlatteningTypeAdapterFactory.INSTANCE)
.create();
Note that the code above has not been extensively tested; there might be bugs or corner cases where it does not work correctly.
I'm trying to deserialize a JSON structure with Jackson and I'm working with a DTO that looks like this:
public class RootLevelDTO {
private List<ComplexEntry> complexEntries;
// ... other fields, not relevant
}
Now, the ComplexEntry can have sub-types, those have properties of enum types etc. A lot can go wrong here if the other side of the communication updates their API and e.g. adds another sub type or adds an enum literal.
What I would like to do is to tell Jackson:
if you encounter any databinding error during deserialization of the complexEntries field...
... do not throw an exception, but instead ignore this entry and continue with the next.
What I tried so far is to use a delegating deserializer for ComplexEntry:
public class ComplexEntryDeserializer extends StdDeserializer<ComplexEntry> {
private StdDeserializer<ComplexEntry> delegate;
public ComplexEntryDeserializer(StdDeserializer<ComplexEntry> delegate){
this.delegate = delegate;
}
public ComplexEntry deserialize(JsonParser p, DeserializationContext ctxt){
try {
return this.delegate.deserialize(p, ctxt);
}catch(Exception e){
// the list entry failed to deserialize, but we have to return *something* here
return null;
}
}
// ... other mandatory methods, not relevant here
}
This solution has the problem that it will introduce null values to the complexEntries list, which I then have to explicitly get rid of with a Converter.
Is there a more elegant solution to this problem?
After a lot of tinkering I've ended up with the following solution. It doesn't require any additional jackson modules or other magic, only a single (specific) deserializer.
DTO:
public class RootLevelDTO {
// use a custom deserializer for the list
#JsonDeserialize(using = ListOfComplexEntryDeserializer.class)
private List<ComplexEntry> complexEntries;
}
Deserializer:
public class ListOfComplexEntryDeserializer extends JsonDeserializer<List<ComplexEntry>> {
#Override
public List<ComplexEntry> deserialize(JsonParser p, DeserializationContext ctxt) {
List<ComplexEntry> resultList = new ArrayList<>();
while(p.nextToken() != JsonToken.END_ARRAY){
try {
// delegate the deserialization of the individual list entries to the standard deserializers
resultList.add(ctxt.readValue(p, ComplexEntry.class))
}catch(Exception e){
// log that the entry wasn't deserialized properly
System.out.println("ComplexEntry could not be read and will be ignored.");
}
}
return resultList;
}
}
Big disclaimer: While the code above works, it's not something you should go for by design. I'm really with my back to the wall here and have no other choice (due to external factors beyond my control), and for that case it works.
I want to use Jackson to implement toString() to return the JSON representation of an object, but I do not want to use any Jackson annotation in my code.
I tried an implementation along the lines of:
public String toString()
{
Map<String,Object> ordered = ImmutableMap.<String, Object>builder().
put("createdAt", createdAt.toString()).
put("address", address.toString()).
build();
ObjectMapper om = new ObjectMapper();
om.enable(SerializationFeature.INDENT_OUTPUT);
try
{
return om.writeValueAsString(object);
}
catch (JsonProcessingException e)
{
// Unexpected
throw new AssertionError(e);
}
}
This works well for simple fields but if "address" has its own fields then instead of getting this:
{
"address" : {
"value" : "AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ",
"tag" : null
}
I get this output instead:
{
"address" : "{\n\"value\" : \"AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ\",\n \"tag\" : null"
}
In other words, the address value is being treated like a String as opposed to a JsonNode.
To clarify:
On the one hand, I want to control how simple class fields are converted to String. I don't want to use Jackson's built-in converter.
On the other hand, for complex fields, returning a String value to Jackson leads to the wrong behavior.
I believe that I could solve this problem by adding a public toJson() method to all my classes. That method would return a Map<String, JsonNode>, where the value is a string node for simple fields and the output of toJson() for complex fields. Unfortunately, this would pollute my public API with implementation details.
How can I achieve the desired behavior without polluting the class's public API?
UPDATE: I just saw an interesting answer at https://stackoverflow.com/a/9599585/14731 ... Perhaps I could convert the String value of complex fields back to JsonNode before passing them on to Jackson.
I think you should implement two methods in each class - one to dump data, second to build JSON out of raw data structure. You need to separate this, otherwise you will nest it deeper and deeper every time you encapsulate nested toString() calls.
An example:
class Address {
private BigDecimal yourField;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("yourField", this.yourField.toPlainString());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}
class Employee {
private Address address;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("address", this.address.toMap());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}
I'm using Gson to parse responses from a server on Android. Each response has some useless (to me) data on it that complicates my Gson models. Here is the general hierarchy of json returned:
response: {
date: 1406253006807,
otherUselessData1: "This is some useless data",
otherUselessData2: "This is some useless data",
usefulJsonObject: { <---- This is really the object that I care about
}
}
Everything above or at the same level as usefulJsonObject I could really do without. The useless data is returned for every request, and the actual response is embedded beneath as the usefulJsonObject. This wouldn't be a big problem but it's really cluttering up my gson model objects.
For example:
Let's say I have 3 requests I can make: A, B, and C. For each response it seems I need to make a minimum of 3 custom classes.
public class ResponseA {
#SerializedName("response") ResponseObjectA responseObject;
public static class ResponseObjectA {
#SerializedName("usefulJsonObject") UsefulObjectA usefulObject;
}
public static class UsefulObjectA {
}
}
I've tried a few solutions, but I haven't found anything elegant that wouldn't add an extra step to my process. I'm using retrofit to do my http requests and it's really nice that it just returns the fully parsed gson object to me. I've thought of other solutions like having the useful object just be a JsonElement and then doing a 2nd gson call after the first comes back. Again, not ideal.
I just wanted to know if I was missing something. Surely I'm not the only one who's encountered something like this, and so I thought I'd ask how other people would handle something like this.
It is initialization Instance value, not NULL value. Check my example.
Address.java
public class Address {
public Address(){
}
}
Person.java
public class Person {
private String name;
private String nrc;
private Address address;
public Person(String name, String nrc, Address address) {
this.name = name;
this.nrc = nrc;
this.address = address;
}
}
The following Json string is equalvent to
Person person = new Person("Zaw Than Oo", "11111", null);
{
"name": "Zaw Than Oo",
"nrc": "11111"
}
The following Json string is equalvent to
Person person = new Person("Zaw Than Oo", "11111", new Address());
{
"name": "Zaw Than Oo",
"nrc": "11111",
"address": {} <-- here use less object for you.
}
Even if you don't create new Instance, Other lib/api(you used) may be create that instance by Reflection.
Short to the Point
{
...
"xxx": {} --> new instance without data/value
...
}
{
...
--> null value
...
}
I never found an elegant way dealing with just Gson. I tried several options with Generics, all of which didn't work or left something to be desired.
Since I'm using Retrofit, I decided to override the GsonConverter, and just filter out the unnecessary information from all my requests. It ends up not being as flexible, as in I can't use the same Retrofit network interface for calls to other servers, but I'm not really doing that, and it also has the down side of having 2 rounds of json parsing calls (meh). You could probably do this more efficiently, but this is working for me for now.
public class CustomGsonConverter extends GsonConverter {
private Gson mGson;
public CustomGsonConverter(Gson gson) {
super(gson);
this.mGson = gson;
}
public CustomGsonConverter(Gson gson, String encoding) {
super(gson, encoding);
this.mGson = gson;
}
#Override public Object fromBody(TypedInput body, Type type) throws ConversionException {
try {
CustomResponse customResponse = mGson.fromJson(new InputStreamReader(body.in()), CustomResponse.class);
return mGson.fromJson(customResponse.responseObject.data, type);
} catch (IOException e) {
throw new ConversionException(e);
}
}
public static class CustomResponse {
#SerializedName("rsp") ResponseObject responseObject;
public static class ResponseObject {
// #SerializedName("date") long date;
#SerializedName("data") JsonElement data;
}
}
}
Maybe there is a better way that I'm just not realizing.
I have two java classes for parsing jason into java alone. Beyond that, the classes are not used for any thing. Below are the two classes.
import java.util.ArrayList;
public class PaymentsPaid {
public ArrayList<PaidDetailAmounts> amount;
}
and
public class PaidDetailAmounts {
public Long invoiceFeeId;
public Double amountPaid;
}
Here is where the string and the use of and object mapper.
"amount": [{"invoiceFeeId": 12085, "amountPaid": 100},{"invoiceFeeId": 12084, "amountPaid": 100},{"invoiceFeeId": 12086, "amountPaid": 500}]
and the mapper code
ObjectMapper mapper = new ObjectMapper();
try {
PaymentsPaid paymentsPaidModel = mapper.readValue(httpServletRequest.getParameter("amount"), PaymentsPaid.class);
/*
Iterator<PaidDetailAmounts> iterator = paymentsPaidModel.amount.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().invoiceFeeId);
}
*/
} catch (JsonParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
This is my exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "
invoiceFeeId" (Class PACKAGE_NAME.PaymentsPaid), not marked as ignorable
I must be doing something worng, because I built a search feature using this approach and it is currently in my application and working well. Please advise. I think it may be a mal formed json string, because it should be an array.
The Problem seams to be that jackson tryes to access the invoiceFeeId field of class PaymentsPaid, but it is a field of class PaidDetailAmounts.
I think there is some surrounding brackets missing in your Json string:
{ // <-- missing?
"amount": [{"invoiceFeeId":...}]
} // <-- missing?
But I am not an JSON expert, so I would try to write an simple test case that create a JSON string from some Java Objects and then parse this strings to back to Java Objects. So that the test can assert that both (sets of) objects are equals.
Then you can use the JSON String created by the test and compare it with your input, I would expect that the (missing) brackets are the difference between them.