How to map a Java Map to Object using Orika - java

I am trying to map a Java Map to a POJO but having problems with using Orika.
I have a LinkedHashMap and trying to map it to a POJO. I've been reading this website https://www.baeldung.com/orika-mapping, specifically section 4.2
This is how I define my orika mapper:
factory.classMap(Map.class, TestDto.class)
.field("nest['name']", "name")
.toClassMap();
and this is the LinkedHashMap I'm trying to map:
Map<Object, Object> nest = new LinkedHashMap<>();
nest.put("name", "myname");
Map<Object, Object> obj = new LinkedHashMap<>();
obj.put("nest", nest);
and this is the POJO I'm trying to map to:
public class TestDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
when I do:
TestDto testDto = mapper.map(obj, TestDto.class);
I get null as name. Is this scenario possible to map or do I have to do more customization of orika?

Yeah the name will return null for this mapping.
TestDto testDto = mapper.map(obj, TestDto.class);
This is due to variable name in TestDto class does not contain variable name nest.
To map your POJO with different key.
Pleas add #JsonProperty in your get() method.
Example:
#JsonProperty("nest")
public String getName() { return name; }

I figured it out, you can define a mapper like:
factory.classMap(Map.class, TestDto.class)
.field("nest:{get('nest')|type=Map}.name:{get('name')|type=java.lang.String}", "name")
.toClassMap();
which is outlined here:
https://orika-mapper.github.io/orika-docs/advanced-mappings.html
This is really only needed if you don't have control over the Pojo and/or do not want to make a copy of the Pojo

Related

How to create query param body with multiple key param values

In the below hashmap you can see, I have list of key param values for which I need to automate cases for multiple values without repeating the hashmap rather it would be and update.
How I am doing it:
1st test case
HashMap<String, String> queryParam = new HashMap<>();
queryParam.put("Name", Name);
queryParam.put("street","street" );
queryParam.put("city","city" );
queryParam.put("state", "state");
queryParam.put("postalCode","postalCode" );
queryParam.put("country", "country");
queryParam.put("email", "email");
queryParam.put("website","website" );
queryParam.put("phone", "phone");
Response response = request.auth().basic(uname, pwd).body(queryParam).contentType(APPLICATION_JSON)
.post().then().extract()
.response();
Now if you see the above hashmap, it has mandatory params, some optional and then each param has different validation. Now it terms to cover all the testcases with each keys, above haspmap is repeating and values or keys are changing. I would like to do this in better and efficient way of it.
Instead of using Map<K, V>, you should use Java POJO. Using constructor to setup default value, then using setter to change value. It's more efficient.
One more thing, you could apply Factory design pattern to build object with desired value for each test case.
Test example
#Test
void test1() {
QueryObject query = QueryObjectFactory.getDefaultValue();
Response res = given().contentType(ContentType.JSON)
.body(query)
.post("to_your_api_endpoint");
}
Factory class
public class QueryObjectFactory {
public static QueryObject getDefaultValue() {
QueryObject queryObject = new QueryObject();
queryObject.setName("name");
queryObject.setStreet("street");
queryObject.setCity("city");
queryObject.setCountry("country");
queryObject.setState("state");
queryObject.setPostalCode("postalCode");
queryObject.setEmail("email");
queryObject.setWebsite("website");
queryObject.setPhone("phone");
return queryObject;
}
}
POJO
note: I use lombok to generate getter and getter --> reduce complex of POJO class.
import lombok.Data;
#Data
public class QueryObject {
private String name;
private String street;
private String city;
private String state;
private String postalCode;
private String country;
private String email;
private String website;
private String phone;
}

How to declare a variable or Object of any class type in Java

I am quite new to Java and I am trying to deserialize the JSON using Jackson and I facing some minor issue with regards to declaring the Object/Variable type. I will provide all the codes then explain the issue for easy understanding.
I have an enum that will have the required type values:
public enum IdentifierTypeValues {
Type1,
Type2,
Type3,
//Constructor and Getter of enum values
}
Then for each of these type, I have different classes which will have different input and do a completely different type of process:
public class GenerateType1 {
private String name;
private String age;
//Getter and Setter
//Some required process based on these values
}
public class GenerateType2 {
private String address;
private String city;
private String country;
//Getter and Setter
//Some required process based on these values
}
public class GenerateType3 {
private String firstName;
private String lastName;
private String fullName;
//Getter and Setter
//Some required process based on these values
}
Now I have a wrapper class for these type of classes which will take the type based on enum and typeInfo values. I want the typeInfo values to be any of the class based type something like this:
public class TypeSyntax {
private IdentifierTypeValues indeitiferType;
private GenerateType1 / GenerateType2 / GenerateType3 identifierTypeValues;
//Here the identifierTypeValues can have the values for anytype
//How to declare a variable of any of these class type?
}
This is the class that will be used by my JSON for deserializing. I know I can add a wrapper class of those 3 types and provide that wrapper class as a type class for this. Something like this:
public class WrapperClass{
private GenerateType1 type1;
private GenerateType2 type2;
private GenerateType3 type3;
}
public class TypeSyntax{
private IdentifierTypeValues indeitiferType;
private WrapperClass identifierTypeValues;
//But using this approach will change my JSON structure which I do not want to do.
}
My JSON structure is something like this and I would like to keep it in the same way.
{
"indeitiferType":"Type1",
"identifierTypeValues":{
"name":"Batman",
"age":"2008"
}
}
Is there a way I can declare the variable of multiple type class? or any better approach to handle this by keeping the json format same? I tried searching but I am unable to search what exactly so any help would be really appriciated.
Because the type identifier exists on a different level than the other properties a wrapper class TypeSyntax needed. There are several open feature requests to add wrapping functionality to Jackson e.g. https://github.com/FasterXML/jackson-databind/issues/512
Fortunately polymorphism is supported in Jackson with #JsonTypeInfo and #JsonSubTypes annotations.
Wrapper class should look like:
public class TypeSyntax {
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "identifierType")
private GenerateTypeBase identifierTypeValues;
// getters and setters (omitted for brevity)
}
GenerateTypeBase is the common parent class
#JsonSubTypes({
#JsonSubTypes.Type(value = GenerateType1.class, name = "Type1"),
#JsonSubTypes.Type(value = GenerateType2.class, name = "Type2"),
})
public abstract class GenerateTypeBase {
private String name;
private String age;
// getters and setters (omitted for brevity)
}
In this different children classes will instantiated based on the identifierType property.
The children must extend this base class:
public class GenerateType2 extends GenerateTypeBase {
// additional properties
}
In a short test it will be:
#Test
void wrapperTest() throws IOException {
ObjectMapper mapper = new ObjectMapper();
GenerateType2 a = new GenerateType2();
a.setName("Foo");
a.setAge("13");
TypeSyntax w = new TypeSyntax();
w.setIdentifierTypeValues(a);
String json = mapper.writeValueAsString(w);
System.out.println(json);
}
and the output:
{
"identifierTypeValues":
{
"name":"Foo",
"age":"13"
},
"identifierType":"Type2"
}
Deserialization
#Test
void wrapperTest() throws IOException {
ObjectMapper mapper = new ObjectMapper();
String input = "{\"identifierTypeValues\": \"name\":\"Foo\",\"age\":\"13\"},\"identifierType\":\"Type2\"}";
TypeSyntax w = mapper.readValue(new StringReader(input), TypeSyntax.class);
assertAll(
() -> assertEquals(GenerateType2.class, o.getIdentifierTypeValues().getClass()),
() -> assertEquals("13", o.getIdentifierTypeValues().getAge())
);
}
If you want more flexibility you can write custom (de)serializer and / or custom resolver. Using custom TypeIdResolver that will possible to convert identifiers to types programmatically instead of using "key-value pairs" in #JsonSubTypes

Make #JsonAnySetter work with #Value Lombok

I have a json object with a lot of properties (~80 properties) I want to deserialize in a POJO without creating manually all the properties. I was able to do this by using the #JsonAnySetter with a Map property like described here.
Now I want to make this work by making my POJO immutable using Lombok.
I tried this but it does only deserialize the id and code properties. Any idea on how to make it work?
#Value
#Builder
#EqualsAndHashCode
#JsonDeserialize(builder = Product.ProductBuilder.class)
class Product {
#JsonProperty
private String id;
#JsonProperty
private String code;
#Getter(AccessLevel.NONE)
#Builder.Default
#JsonProperty
private Map<String, Optional<Object>> any = new HashMap<>();
#JsonAnyGetter
public Map<String, Optional<Object>> getAny(){
return this.any;
}
#JsonAnySetter
public void setAny(String key, Optional<Object> value){
this.any.put(key, value);
}
}
Update 2021-02-01: Lombok v1.18.16
Starting with v1.18.16, Lombok automatically copies #JsonAnySetter to the #Singular methods in builder. In combination with #Jacksonized you can simply use this code:
#Value
#Jacksonized
#Builder
class Product {
private String id;
private String code;
#JsonAnySetter
#Singular("any")
private Map<String, Object> any;
}
Older Lombok versions
For previous Lombok version, this requires some customization of the generated builder class.
Customizing a lombok builder can be done by simply adding its inner class header to your class. Lombok detects that there is already a builder class and just adds all the things that are not already present. This means you can add your own methods, and if those happen to have the same name than a method that lombok would generate, lombok skips this method.
With this approach, we replace the builder's setter method for "any", adding the required #JsonAnySetter to it. I use a LinkedHashMap as map in case the order is relevant; you can use a regular HashMap if it's not.
Furthermore, we replace the build() method to make sure the map you supply to the constructor is immutable. I use Guava's ImmutableMap here. This will make the created instance an immutable value.
#Value
#Builder
#JsonDeserialize(builder = Product.ProductBuilder.class)
class Product {
#JsonProperty
private String id;
#JsonProperty
private String code;
#Getter(onMethod_ = #JsonAnyGetter)
private Map<String, Object> any;
#JsonPOJOBuilder(withPrefix = "")
public static class ProductBuilder {
#JsonAnySetter
public ProductBuilder any(String anyKey, Object anyValue) {
if (this.any == null) {
this.any = new LinkedHashMap<String, Object>();
}
this.any.put(anyKey, anyValue);
return this;
}
public Product build() {
return new Product(id, code, any == null ? ImmutableMap.of() : ImmutableMap.copyOf(any));
}
}
}

Maintaining ordering of Class fields during Json Deserialization

Lets say I have a class as follows:
#JsonIgnoreProperties(ignoreUnknown = true)
class MyClass {
#JsonProperty(value="vertical")
private String vertical;
private String groupId;
#JsonProperty(value = "relationships")
private void unwrapGroupId(Map<String, Map<String, List<Map<String, Object>>>> relationships) {
this.groupId = ""; // Some logic to process the relationships map & set this.groupId based on the value set in this.vertical during deserialization
}
}
When deserializing an API Response to MyClass, is it guaranteed that vertical field is set before unwrapGroupId() is processed???? Else my processing in unwrapGroupId() would fail as this.vertical will be empty. If not , how can it be achieved.
I looked up #JsonPropertyOrder, but looks like it doesnt solve this usecase
Note: I use Jackson 2.8.1

Deserializing JSON object with dynamic JsonProperty without wrapper class using Jackson

I want to restructure an application so that it uses REST instead of an EJB3Factory which wasn't needed in the first place and only makes trouble with the new server.
Basically I have a class Request:
public class Request {
public String name;
public String id;
public List<? extends someObject> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The Request gets created and using Gson made into a Json object:
Gson gson = new Gson();
String payload = gson.toJson(Request);
This then gets sent to the REST API on the server. There Jackson deserializes it. I do not have access to the Jackson implementation there and cannot change it to Gson.
What I am basically trying to do now is to get Jackson to use the non-default constructor to deserialize the object. I know I can annotate the non-default constructor like this:
#JsonCreator
public Request(#JsonProperty("name") String name, #JsonProperty("id")
String id, #JsonProperty("list") List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The thing is though that the field name of list is set at runtime per reflection and the Gson object that is generated might have it as scenarioName1 for one and scenarioName2 for something else.
I have looked at the different solutions provided here on Stack Overflow but none of them could provide me with a solution for my problem. This seemed most helpful but I cannot under any circumstances use a wrapper property nor can I actually map all possibilities.
Anyone got any idea?
EDIT to include examples:
Example 1:
{"name":"someName","id":"first","someScenarioName":[{...}]}
Example 2:
{"name":"someOtherName","id":"second","differentScenarioName":[{...}]}
Since I'm out of town on business that is the best I can do with right now. It's basically the last field having a different name depending on which scenario was chosen beforehand.
Maybe you can try take a look on Mapper Features. Sincerely I didn't try it yet because I'm at work and so on, but I will send now my example and maybe it can help you:
public class Request {
public String name;
public String id;
public List<? extends T> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
}
Then to deserialize the object:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.USE_ANNOTATIONS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.readValue(json, Request.class);
My try is because the deserialization by annotation is true by default, but once you don't have a "list" object most of time, it won't find the field there.
Okay, so I figured out what my problem was. There are other lists in the class and those were the trouble. After annotating each of them with #JsonProperty("theirRespectiveName") it worked like a charm... Now I have to annotate about 100 lines of code and solve some more problems.

Categories

Resources