I am trying to use RestTemplate to call a webservice, currently I am using the Object type rather than a concrete user defined one which is what I want to do.
Currently the response from the web service is:
{Locales=[{Code=ar-AE, Name=العربية (الإمارات العربية المتحدة)}, {Code=az-AZ, Name=Azərbaycanılı (Azərbaycan)}, {Code=bg-BG, Name=български (България)}]}
I am currently doing this:
Object locales = restTemplate.getForObject(localeUrl, Object.class, apiKey);
which is I want to be able to map it to a class that I have defined, but not sure how my class should be laid out, my class currently looks like this:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "Locales")
#XmlAccessorType(XmlAccessType.FIELD)
public class Locales {
private List<Locale> Locales = new ArrayList<>();
private Locales(){};
public List<Locale> getLocales() {
return Locales;
}
public void setLocales(ArrayList<Locale> newLocales) {
this.Locales = newLocales;
}
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Locale {
private String Code;
private String Name;
private Locale(){}
public String getCode() {
return this.Code;
}
public void setCode(String Code) {
this.Code = Code;
}
public String getName() {
return this.Name;
}
public void setName(String Name) {
this.Name = Name;
}
}
Use below code for calling API -
Locales locales = restTemplate.getForObject(localeUrl, Locales.class, apiKey);
Create one class Locales -
#XmlRootElement(name = "Locales")
#XmlAccessorType(XmlAccessType.FIELD)
public class Locales{
private List<Locale> locales = new ArrayList<>();
// getter and setter
}
Related
I had a DTO that was using Lombok functionaliy as shown below.But now due to some requirement I had to extend my DTO to a parent class which looks like below.How can I do minimal change in my DTO to support that.I tried using #SuperBuilder annotation but it failed.
DTO Before:
#Getter
#ToString
#EqualsAndHashCode
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage {
private final String name;
}
Parent Class that needs to be extended
#Data
#SuperBuilder(toBuilder = true)
#JsonDeserialize(builder = MyDTO.Builder.class)
public abstract class MyDTO implements Serializable {
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
protected String myAccountId;
protected MyDTO() {}
public static int hashCode(Object... objects) {
return Arrays.deepHashCode(objects);
}
public static boolean equal(Object o1, Object o2) {
// implementation of equals method
}
public abstract String emitSerializedPayload() throws JsonProcessingException;
#JsonPOJOBuilder(withPrefix = "")
protected abstract static class Builder<T extends MyDTO, B extends Builder<T, B>> {
protected T dtoInstance;
protected B builderInstance;
public Builder() {
dtoInstance = createDtoInstance();
builderInstance = returnBuilderInstance();
}
protected abstract T createDtoInstance();
protected abstract B returnBuilderInstance();
public B myAccountId(String accountId) {
dtoInstance.myAccountId = accountId;
return builderInstance;
}
public T build() {
return dtoInstance;
}
}
}
I tried to build RequestMessageClass manually and it works fine but there are lot of classes in my application and I dont want to change them manually, how can I change my existing RequestMessage class with annotations or some minimum change to get it working.
This is what I tried but I am getting compilation error when doing this
RequestMessage.Builder().name(myName).myAccountId(myAcId).build();
What I tried is like shown below:
#Getter
#ToString
#EqualsAndHashCode(callSuper = true)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage extends MyDTO {
private final String name;
#Override
public String emitSerializedPayload() throws JsonProcessingException {
// TODO Auto-generated method stub
return null;
}
}
You shouldn't mix lombok #Builder with static inner Builder class. If it is possible to get rid of Builder class, the next code should work.
RequestMessage:
#Getter
#ToString
#EqualsAndHashCode(callSuper = true)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage extends MyDTO {
private final String name;
#Override
public String emitSerializedPayload() throws JsonProcessingException {
return null;
}
public RequestMessage(String myAccountId, String name) {
super(myAccountId);
this.name = name;
}
}
MyDTO:
#Data
#SuperBuilder(toBuilder = true)
public abstract class MyDTO implements Serializable {
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
protected String myAccountId;
protected MyDTO() {}
public MyDTO(String myAccountId) {
this.myAccountId = myAccountId;
}
public static int hashCode(Object... objects) {
return Arrays.deepHashCode(objects);
}
public static boolean equal(Object o1, Object o2) {
// implementation of equals method
return false;
}
public abstract String emitSerializedPayload() throws JsonProcessingException;
}
Test:
#Test
void name() {
String myName = "myName";
String myAccountId = "myAccountId";
var request = RequestMessage.builder().name(myName).myAccountId(myAccountId).build();
System.out.println("request = " + request);
RequestMessage requestMessage = new RequestMessage(myAccountId, myName);
}
I use #Builder of lombok project, so consider I have this example:
#Builder
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
}
Which is equivalent to:
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
public static class Builder {
private Integer id;
private String name;
private Builder() {
}
public Builder id(final Integer value) {
this.id = value;
return this;
}
public Builder name(final String value) {
this.name = value;
return this;
}
public Client build() {
return new Client(this);
}
}
public static Client.Builder builder() {
return new Client.Builder();
}
private Client(final Builder builder) {
this.id = builder.id;
this.name = builder.name;
}
}
When I try to set all the the fields in one shot there are no problem:
public static void main(String[] args) {
Client client = Client.builder()
.id(123)
.name("name")
.build();
}
Output:
Client{id=123, name=name}
Now, consider I want to make it in multiple shots. For example:
public static void main(String[] args) {
Client client = Client.builder()
.id(123)//<-------------------------- Set just the id
.build();
client = Client.builder()
.name("name")//<--------------------- Set name
.build();
}
This logically return null for id:
Client{id=null, name=name}
Generally, without lombok, I solve this issue with adding a new constructor in the Builder class which take the same object:
public static class Builder {
// ...
public Builder(Client client) {
this.id = client.id;
this.name = client.name;
}
// ...
}
Then I pass my object to that constructor:
Client client = Client.builder()
.id(123)
.build();
client = new Client.Builder(client)//<---------------- Like this
.name("name")
.build();
This solves my issue, but I can't arrive to solve it with lombok. Is there any way to solve this issue?
You can use the toBuilder property to do that.
#Builder(toBuilder=true)
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
}
and then you can use it like that
public void main(String[] args){
Client client = Client.builder()
.id(123)
.build();
client = client.toBuilder()
.name("name")
.build();
}
Depending on your use case, you can also keep a reference to an existing builder. You can have multiple to the build method.
Disclosure: I am a lombok developer.
I get the below error when I try to deserialize Automobile class. Jackson is trying to search for the field in child element in parent class. How can I make sure jackson uses the appropriate child type for deserialization? I believe I need to use miixins / customer converters. But i'm not sure how to use them in this specific scenario.
Note: In my case all classes except TestMain are in a jar file and I cannot modify the source files.
Error
Exception in thread "main"
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "color" (class com.salesportal.proxy.Car), not
marked as ignorable (one known property: "name"])
Automobile.java
public class Automobile {
private Car[] cars;
public Car[] getCars() {
return cars;
}
public void setCars(Car[] cars) {
this.cars = cars;
}
}
Car.java
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Corolla.java
public class Corolla extends Car {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
TestMain.java
import java.io.IOException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestJSON {
public static void main(String[] args) throws IOException{
ObjectMapper mapper = new ObjectMapper();
Automobile automobile = new Automobile();
Corolla corolla = new Corolla();
corolla.setName("Toyota");
corolla.setColor("Silver Grey");
automobile.setCars(new Corolla[]{corolla});
System.out.println(mapper.writeValueAsString(automobile));
Automobile auto = mapper.readValue(mapper.writeValueAsString(automobile), Automobile.class);
}
}
JSON String
{"cars":[{"name":"Toyota","color":"Silver Grey"}]}
Vicky , you can use sub type annotations in JACKSON. The below works for me , with just this change
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes({ #JsonSubTypes.Type(value = Corolla.class, name = "color") })
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Automobile class doesn't have color property.
Change this :
Automobile auto = mapper.readValue(mapper.writeValueAsString(automobile), Automobile.class);
to this :
Corolla auto = mapper.readValue(mapper.writeValueAsString(automobile.getCars()), Corolla .class);
We have a structure that represents configuration of some sort. We have had a typo in the word periodicity, it was wrongly spelled with 'o' as period*o*city. Below example source is the corrected one. However, I need to be able to read the old configuration files to maintain backwards compatibility.
Can I make JSON Jackson recognize the misspelled field/property on deserialization but ignore it on serialization?
We are using version 2.6.6 of JSON Jackson.
package foo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
#JsonInclude(Include.NON_EMPTY)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Rule {
private LogPeriodicity periodicityLevel;
private Integer periodicity;
// ctors and some other methods omitted for brevity
public LogPeriodicity getPeriodicityLevel() {
return periodicityLevel;
}
public void setPeriodicityLevel(LogPeriodicity periodicityLevel) {
this.periodicityLevel = periodicityLevel;
}
public Integer getPeriodicity() {
return periodicity;
}
public void setPeriodicity(Integer periodicity) {
this.periodicity = periodicity;
}
}
If i got your question right you want something like this?
MyClass obj = mapper.readValue("{ \"name\" : \"value\"}", MyClass.class);
String serialized = mapper.writeValueAsString(obj);
MyClass obj2 = mapper.readValue("{ \"name2\" : \"value\"}", MyClass.class);
String serialized2 = mapper.writeValueAsString(obj2);
if( Objects.equals(serialized2, serialized))
System.out.println("Success " + serialized + " == " + serialized2 );
if you don't want extra field in POJO you can just add setter like this:
public static class MyClass {
#JsonProperty
private String name = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonSetter
public void setName2(String name2) {
setName(name2);
}
}
You can probably also register legacy Mixin instead of #JsonSetter
public abstract class LegacyMyClassMixIn{
#JsonProperty("name")
private String name;
#JsonGetter("name")
public abstract String getName();
#JsonSetter("name2")
public abstract void setName(String name) ;
}
And use it like this:
SimpleModule module = new SimpleModule();
module.setMixInAnnotation(MyClass.class, LegacyMyClassMixIn.class);
mapper2.registerModule(module);
Btw in Gson it can be done with just 1 line #SerializedName(value="name", alternate={"name2"}) public String name = null;
Input paramter to my webservice method is an Object of Class AddSingleDocRequest. This class contains all the input fields as class instance variable with their getter and setter. I want to make some of the input fields mandatory. What is the best way to achieve this ?
Following is the code snippet:
**//webservice method
public String uploadDoc(AddSingleDocRequest request)
{
}
**//Request Class**
public class AddSingleDocRequest
{
private String sFilepath;
private String sDataClass;
public void setDataClassName(String dataClassName)
{
this.sDataClass= dataClassName;
}
public String getDataClassName() {
return sDataClass;
}
public void setFilePath(String filePath)
{
this.sFilepath=filePath;
}
public String getFilePath()
{
return sFilepath;
}
}
I want to make sFilePath parameter as mandatory.
Add the next JAX-B annotations:
#XmlType(name = "AddSingleDocRequestType", propOrder = {
"sFilepath", "sDataClass"
})
public class AddSingleDocRequest {
#XmlElement(name = "sFilepath", required = true)
private String sFilepath;
#XmlElement(name = "sDataClass", required = false)
private String sDataClass;
public void setDataClassName(String dataClassName) {
this.sDataClass = dataClassName;
}
public String getDataClassName() {
return sDataClass;
}
public void setFilePath(String filePath) {
this.sFilepath = filePath;
}
public String getFilePath() {
return sFilepath;
}
}
See more in Using JAXB to customize mapping for JAX-WS web services.