I have a complex object and for some of the nested objects I need to serialize them into JSON fields instead of JSON objects.
Eg.
public class Outer {
private String someField;
private AnotherClass anotherField;
}
public class AnotherClass {
#XmlElement(name = "useThisName")
private String someField;
private String anotherField;
}
How can I make a custom serializer that will be for the nested object and obey the annotations so the fields are named properly?
My use case for this is to use the ObjectMapper.convertValue() method to create a Map so that I can loop through it and create NameValuePairs for a rest url.
In the end I am hoping to end up with a
Map<String, String>
That I can loop over and create apache BasicNameValuePairs from.
Below is some code I want to use for the end result if I can get everything to serialize properly.
Map<String, String> parameters
= DefaultJacksonMapper.getDefaultJacksonMapper().convertValue(obj, LinkedHashMap.class);
return parameters
.entrySet()
.stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
If I convert this to a map now my output is like:
"someField" -> "data"
"anotherField" -> "size = 2"
I am trying to get the Map to have the following output which I feel like I need a custom serializer.
"someField" -> "data"
"useThisName" -> "data"
"anotherField" -> "data"
Ok I figured this out.
I ended up creating a new Module that inherited off of SimpleModule. Then I created a new Abstract class like
public abstract class OuterMixin {
#JsonUnwrapped
private AnotherClass anotherField;
}
I also had to annotate the AnotherClass with JsonProperty Like:
public class AnotherClass {
#XmlElement(name = "useThisName")
#JsonProperty("useThisName")
private String someField;
private String anotherField;
}
The when I got my Object Mapper I just registered my module with it and did the conversion and it all worked out.
As a side note I have another property that I had to write a custom serializer for and the #JsonUnwrapped did not work with that.
Related
Let's say I have simple HashMap:
Map<String, String> map = new HashMap<>();
map.put("field1","value1");
map.put("field2", "value2");
I also have simple java class:
class SimpleClass {
public String field1;
public String field2;
}
What is simplest and most elegant way to create SimpleClass instance with corresponding fields/values taken from map? In this case, resulting SimpleClass instance should get field1 value 'value1andfield2valuevalue2`.
SimpleClass is already defined, now we need to find matching keys in map, if match found, it's value should be assigned to corresponding class field.
In my real application, I will get list of maps and I need to transform it into List<SimpleClass>. Map can contain additional keys, that need to be ommited (if no matching class field is available).
Can I use (for example) Guava to make transoformation like this? I'm on Android so java streams can't be used so far.
[edit]
My attempt:
private SimpleClass mapToObject(Map<String, String> map)
{
SimpleClass result = new SimpleClass();
for(Field f: result.getClass().getDeclaredFields())
{
try
{
f.setAccessible(true);
f.set(result,map.get(f.getName()));
}
catch (Exception e)
{
Log.d("error", e.toString());
}
}
return result;
}
Use ObjectMapper ,
SimpleClass simpleClass = objectMapper.convertValue(map, SimpleClass.class);
Here is my POJO:
public class Target {
#JsonProperty(value="sizes",required=true)
private Set<Size> Sizes;
#JsonProperty(value="domains",required=true)
private Set<String> Domains;
// getters and setters
}
I initialize this POJO like this:
Target target = new Target(new HashSet<>(), new HashSet<>());
How to get Json from this object with empty fields? I mean smth. like that:
{"sizes":[], "domains":[]}
I tried use ObjectMapper with different parameters, but it was no useful..
I have a class like this:
#JsonInclude(Include.NON_NULL)
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class VPC
{
#NotNull()
private String id;
#NotNull()
#DynamoDBMarshalling(marshallerClass = SubnetTypeMarshaller.class)
private Map<SubnetType, List<String>> subnetTypeToId;
}
Here, SubnetType is a enum like this:
public enum SubnetType
{
AppSubnet,
DBSubnet,
DMZSubnet;
}
Now, I want to store the above in AWS DynamoDB. For this, I need to convert enum to a string and I have written the following.
public class SubnetTypeMarshaller implements DynamoDBMarshaller<Map<SubnetType, List<String>>>
{
private Gson gson = new GsonBuilder().create();
#Override
public String marshall(final Map<SubnetType, List<String>> securityGroupTypeListMap)
{
return gson.toJson(securityGroupTypeListMap);
}
#Override
public Map<SubnetType, List<String>> unmarshall(final Class<Map<SubnetType, List<String>>> aClass,
final String s)
{
return gson.fromJson(s, aClass);
}
}
But this doesn't work. While getting values from DB, I get the following error:
java.lang.String cannot be cast to java.lang.Enum (through reference chain: java.util.ArrayList[0]->["security_group_type_to_id"])
Am I missing something in this? I searched on other posts how to convert enums to string using #SerializedName annotation. But that didn't work either. I have also tried the solution mentioned in the other post, but it doesn't work. Maybe because my enum is in itself a part of map and I can't really annotate the enum attribute inside the map.
Gson provides default serialization for enums, if you want to change it you have to build your own adapter.
Check gson docs for registerTypeAdapter.
Let's imagine you have the following JSON:
{
"prop0": "value0",
"level1" : {
"prop1": "value1"
"prop2": "value2"
},
....
}
Can it be turned to simple Java object?
class Pojo {
private String prop0;
private String prop1;
private String prop2;
}
I don't want to create a intermediate class to wrap "level1".
What is come to my mind is to map my class in this way:
class Pojo {
private String prop0;
#JsonProperty("level1.prop1")
private String prop1;
#JsonProperty("level1.prop2")
private String prop2;
}
But unfortunately it doesn't work. The inverse problem - turn complex Java object to plain JSON can be simply solved using #JsonUnwrapped annotation.
Can you please suggest any workable solution for my issue?
You need to either write a custom deserializer, or add a setter that can transform the structure. For latter you could do something like
...
public void setLevel1(Map<String,String> values) { // or JsonNode
prop1 = values.get("prop1");
// and so forth; if names are regular, can use looping
}
I'm calling a rest service that returns a json object. I'm trying to deserialize the responses to my Java Beans using Jackson and data-binding.
The example Json is something like this:
{
detail1: { property1:value1, property2:value2},
detail2: { property1:value1, property2:value2},
otherObject: {prop3:value1, prop4:[val1, val2, val3]}
}
Essentially, detail1 and detail2 are of the same structure, and thus can be represented by a single class type, whereas OtherObject is of another type.
Currently, I've set up my classes as follows (this is the structure I would prefer):
class ServiceResponse {
private Map<String, Detail> detailMap;
private OtherObject otherObject;
// getters and setters
}
class Detail {
private String property1;
private String property2;
// getters and setters
}
class OtherObject {
private String prop3;
private List<String> prop4;
// getters and setters
}
Then, just do:
String response = <call service and get json response>
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(response, ServiceResponse.class)
The problem is I'm getting lost reading through the documentation about how to configure the mappings and annotations correctly to get the structure that I want. I'd like detail1, detail2 to create Detail classes, and otherObject to create an OtherObject class.
However, I also want the detail classes to be stored in a map, so that they can be easily distinguished and retrieved, and also the fact that the service in in the future will return detail3, detail4, etc. (i.e., the Map in ServiceResponse would look like
"{detail1:Detail object, detail2:Detail object, ...}).
How should these classes be annotated? Or, perhaps there's a better way to structure my classes to fit this JSON model? Appreciate any help.
Simply use #JsonAnySetter on a 2-args method in ServiceResponse, like so:
#JsonAnySetter
public void anySet(String key, Detail value) {
detailMap.put(key, value);
}
Mind you that you can only have one "property" with #JsonAnySetter as it's a fallback for unknown properties. Note that the javadocs of JsonAnySetter is incorrect, as it states that it should be applied to 1-arg methods; you can always open a minor bug in Jackson ;)