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);
Related
Edit:
The problem could be isolated and is described here Jackson: Deserialize to a Map<String, Object> with correct type for each value
First off, there is a work around using enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY);. But that is not really applicable because the method is deprecated for obvious reasons and it also adds unnecessary type information.
Edit:
I stripped down the code to a bare minimum and I also found the problem. The container has Object as values which Jackson seems to treat as simple Object during serialization. That might be the reason why the annotations at class level are not being considered.
#JsonProperty("simulation_parameters")
private final Map<Parameter<?>, Object> simulation_parameters;
The resulting JSON looks like this after serialization:
{
"simulation_parameters" : {
"INPUT_HOUSEHOLDS" : "input_hh",
"INPUT_PERSONS" : "input_p"
}
}
Changing the container to:
#JsonProperty("simulation_parameters")
private final Map<Parameter<?>, DataResource> simulation_parameters;
Results in:
{
"simulation_parameters" : {
"INPUT_HOUSEHOLDS" : {
"#type" : "database",
"resource_name" : "public.households"
},
"INPUT_PERSONS" : {
"#type" : "file",
"filename" : "d:/persons.csv"
}
}
}
Since I have type information in my map keys, is there a way to serialize an entire entry instead of serializing key and value seperately using the #JsonSerialize(keyUsing:..., contentUsing:...)?
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.util.*;
public class JacksonTest {
public static void main(String[] args) throws IOException {
ParameterContainer parameters = new ParameterContainer();
DataResource input_hh = new DatabaseResource("public.households");
parameters.addParameter(Parameters.INPUT_HOUSEHOLDS, input_hh);
DataResource input_p = new FileResource("d:/persons.csv");
parameters.addParameter(Parameters.INPUT_PERSONS, input_p);
ObjectMapper om = new ObjectMapper();
String json_missing_type = om.writerWithDefaultPrettyPrinter().writeValueAsString(parameters);
System.out.println(json_missing_type);
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY, //apparently this is redundant
property = "#type")
#JsonSubTypes({
#JsonSubTypes.Type(value = DatabaseResource.class, name = "database"),
#JsonSubTypes.Type(value = FileResource.class, name = "file")
})
public interface DataResource {
#JsonIgnore
String getResourceName();
}
//data resource as file
#JsonTypeName("file")
public static class FileResource implements DataResource {
#JsonProperty("filename")
private final String filename;
#JsonCreator
public FileResource( #JsonProperty("filename") String filename) {
this.filename = filename;
}
#JsonIgnore
#Override
public String getResourceName() {
return this.filename;
}
}
//data resource as database
#JsonTypeName("database")
public static class DatabaseResource implements DataResource {
#JsonProperty("resource_name")
private final String resource_name;
#JsonCreator
public DatabaseResource(#JsonProperty("resource_name") String resource_name) {
this.resource_name = resource_name;
}
#JsonIgnore
#Override
public String getResourceName() {
return resource_name;
}
}
public static class ParameterDeserializer extends KeyDeserializer {
#Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
Optional<Parameter<?>> defined_parameter = Parameters.values.stream().filter(param -> param.getName().equals(key)).findAny();
if (defined_parameter.isPresent())
return defined_parameter.get();
else
throw new IllegalArgumentException("Parameter: " + key + " is not defined");
}
}
public static class ParameterContainer {
#JsonProperty("simulation_parameters")
#JsonSerialize(typing = JsonSerialize.Typing.DEFAULT_TYPING)
private final Map<Parameter<?>, DataResource> simulation_parameters;
public ParameterContainer() {
this.simulation_parameters = new HashMap<>();
}
#JsonCreator
public ParameterContainer(#JsonProperty("simulation_parameters") Map<Parameter<?>,DataResource> simulation_parameters) {
this.simulation_parameters = simulation_parameters;
}
#JsonIgnore
public void addParameter(Parameter<?> parameter,DataResource value) {
simulation_parameters.put(parameter, value);
}
}
public static class Parameter<T> {
#JsonValue
private final String name;
private final Class<T> object_type;
protected Parameter(String name, Class<T> object_type) {
this.name = name;
this.object_type = object_type;
}
public String getName() {
return name;
}
#JsonIgnore
public Class<T> getObjectType() {
return object_type;
}
}
public static final class Parameters {
public static final Parameter<DataResource> INPUT_HOUSEHOLDS = new Parameter<>("INPUT_HOUSEHOLDS", DataResource.class);
public static final Parameter<DataResource> INPUT_PERSONS = new Parameter<>("INPUT_PERSONS", DataResource.class);
public static final Set<Parameter<?>> values = Set.of(INPUT_HOUSEHOLDS, INPUT_PERSONS);
}
}
I have a JSON file that I am trying to convert into XML using the JAXB annotation approach. Everything is working fine now and I able to convert the JSON to XML. Now I am trying to refactor the code a little bit so that my class would look clean. Hence, I am trying to remove the method which is present in my class and make it JAXB XMLAdapter so that it can be reused by other classes.
Basically I would like to move the XMLSupport method from CarInfo class to XMLAdapter. I am not sure how to populate the CarInfo objects when I move them to the XMLAdapter.
Following is my JSON file (it has been modified for simplicity purpose):
{
"brand": "Ferari",
"build": "Italy",
"engine": "Mercedes",
"year": "2021"
}
Following is the XML that I expect JAXB to provide: (Observe the carInfo tag which is not present in JSON but I need in XML to match the standard XSD)
<?xml version="1.0"?>
<Car>
<brand>Ferari</brand>
<build>Italy</build>
<carinfo>
<engine>Mercedes</engine>
<year>2021</year>
</carinfo>
</Car>
Following are the classes that I have: (Tha Car class that matches the JSON elements)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlTransient
#XmlSeeAlso({MyCar.class});
public class Car{
private String brand;
private String build;
#XmlTransient
private String engine;
#XmlTransient
private String year;
//Getter, Setters and other consturctiores ommited
}
Following is MYCar class that builds the XML by adding the carInfo tag:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Car")
#XmlType(name = "Car", propOrder = {"brand","build", "carInfo"})
public class MyCar extends Car{
#XmlElement(name="carInfo")
private CarInfo carInfo;
public MyCar xmlSupport() {
if(carInfo == null){
carInfo = new Carinfo();
}
carInfo.setEngine(getEngine);
carInfo.setYear(getYear());
return this;
}
}
Following is my CarInfo class which acts as a helper to build the additional tag around MyCar class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {"engine","year"})
public class Carinfo{
private String engine;
private String year;
//Getter, Setters and other consturctiores ommited
}
Following is my Main class which actually builds the XML by using the JAXBCOntext
public class Main{
public static void main(String[] args){
JAXBContext context = JAXBContext.newInstance(MyCar.class);
Marshaller mar = context.createMarshaller();
mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal((MyCar).xmlSupport(), System.out);
System.out.println("-----------------");
}
}
Now coming back to my main question:
As we can see from MyCar class I have the XMLSupport method which is actually populating the CarInfo objects and then using that method I am creating the XML. Is there a way I can move this to XMLAdapter?
I tried creating the XMLAdapter but I am not sure how can I populate the CarInfo objects from the adapter:
public class MyCar extends Car{
#XmlElement(name="carInfo")
#XmlJavaTypeAdapter(ExtensionAdapter.class)
#XmlElement(name = "carInfo")
private CarInfo carInfo;
}
Following is my Adapter class I've tried:
public class ExtensionAdapter extends XmlAdapter<CarInfo, CarInfo> {
#Override
public CarInfo unmarshal(CarInfo valueType) throws Exception {
System.out.println("UN-MARSHALLING");
return null;
}
#Override
public CarInfo marshal(CarInfo boundType) throws Exception {
System.out.println("MARSHALLING");
System.out.println(boundType);
//I get boundType as NULL so I am not sure how to convert the xmlSupport Method to Adapter so I can use this adapter with multiple class
return null;
}
}
You don't need any adapters, you just need a well-defined POJO.
The trick is using getters and setters, not field access, so we can do delegation, and then use #JsonIgnore and #XmlTransient to control which getter/setter methods are used for JSON vs XML.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#XmlRootElement(name = "Car")
#XmlType(propOrder = { "brand", "build", "carinfo" })
#JsonPropertyOrder({ "brand", "build", "engine", "year" })
public final class Car {
#XmlType(propOrder = { "engine", "year" })
public static final class Info {
private String engine;
private String year;
public String getEngine() {
return this.engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getYear() {
return this.year;
}
public void setYear(String year) {
this.year = year;
}
#Override
public String toString() {
return "Info[engine=" + this.engine + ", year=" + this.year + "]";
}
}
private String brand;
private String build;
private Info carinfo;
public Car() {
// Nothing to do
}
public Car(String brand, String build, String engine, String year) {
this.brand = brand;
this.build = build;
this.carinfo = new Info();
this.carinfo.setEngine(engine);
this.carinfo.setYear(year);
}
public String getBrand() {
return this.brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getBuild() {
return this.build;
}
public void setBuild(String build) {
this.build = build;
}
#JsonIgnore // For XML, not JSON
public Info getCarinfo() {
if (this.carinfo == null)
this.carinfo = new Info();
return this.carinfo;
}
public void setCarinfo(Info info) {
this.carinfo = info;
}
#XmlTransient // For JSON, not XML
public String getEngine() {
return getCarinfo().getEngine();
}
public void setEngine(String engine) {
getCarinfo().setEngine(engine);
}
#XmlTransient // For JSON, not XML
public String getYear() {
return getCarinfo().getYear();
}
public void setYear(String year) {
getCarinfo().setYear(year);
}
#Override
public String toString() {
return "Car[brand=" + this.brand + ", build=" + this.build + ", carinfo=" + this.carinfo + "]";
}
}
Test
Car car = new Car("Ferari", "Italy", "Mercedes", "2021");
// Generate JSON
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.enable(SerializationFeature.INDENT_OUTPUT);
String json = jsonMapper.writeValueAsString(car);
// Generate XML
JAXBContext jaxbContext = JAXBContext.newInstance(Car.class);
Marshaller xmlMarshaller = jaxbContext.createMarshaller();
xmlMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
String xml;
try (StringWriter writer = new StringWriter()) {
xmlMarshaller.marshal(car, writer);
xml = writer.toString();
}
// Print generated results
System.out.println(car);
System.out.println(json);
System.out.println(xml);
// Parse JSON
Car carFromJson = jsonMapper.readValue(json, Car.class);
System.out.println(carFromJson);
// Parse XML
Unmarshaller xmlUnmarshaller = jaxbContext.createUnmarshaller();
Car carFromXml = xmlUnmarshaller.unmarshal(new StreamSource(new StringReader(xml)), Car.class).getValue();
System.out.println(carFromXml);
Outputs
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
{
"brand" : "Ferari",
"build" : "Italy",
"engine" : "Mercedes",
"year" : "2021"
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Car>
<brand>Ferari</brand>
<build>Italy</build>
<carinfo>
<engine>Mercedes</engine>
<year>2021</year>
</carinfo>
</Car>
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
As you can see, the generated JSON and XML is exactly what you wanted, and the last two lines of output shows that parsing works as well.
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
}
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;
My JSON:
{
"name": "asdf",
"age": "15",
"address": {
"street": "asdf"
}
}
If street is null, with JsonSerialize.Inclusion.NON_NULL, I can get..
{
"name": "asdf",
"age": "15",
"address": {}
}
But I want to get something like this.. (when address is not null, it is a new/empty object. But street is null.)
{
"name": "asdf",
"age": "15"
}
I thought to have custom serialization feature like JsonSerialize.Inclusion.VALID_OBJECT.
Adding isValid() method in the Address class then if that returns true serialize else don't serialize.
But I don't know how to proceed further/which class to override. Is this possible or any other views on this? Please suggest.
Added classes
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
Customer customer = new Customer();
customer.setName("name");
customer.setAddress(new Address());
mapper.writeValue(new File("d:\\customer.json"), customer);
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Customer {
private String name;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
Note: I am not worrying about deserialization now. i.e, loss of address object.
Thanks in advance.
Customized JSON Object using Serialization is Very Simple.
I have wrote a claas in my project i am giving u a clue that how to Implement this in Projects
Loan Application (POJO Class)
import java.io.Serializable;
import java.util.List;
import org.webservice.business.serializer.LoanApplicationSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
#JsonSerialize(using=LoanApplicationSerializer.class)
public class LoanApplication implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private double amount;
private User borrowerId;
private String businessType;
private String currency;
private int duration;
private Date lastChangeDate;
private long loanApplicationId;
private String myStory;
private String productCategory;
private String purpose;
private Date startDate;
private String status;
private String type;
private String salesRepresentative;
Now LoanApplicationSerializer class that contains the Customization using Serialization Logic................
package org.ovamba.business.serializer;
import java.io.IOException;
import org.webservice.business.dto.LoanApplication;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class LoanApplicationSerializer extends JsonSerializer<LoanApplication> {
#Override
public void serialize(LoanApplication prm_objObjectToSerialize, JsonGenerator prm_objJsonGenerator, SerializerProvider prm_objSerializerProvider) throws IOException, JsonProcessingException {
if (null == prm_objObjectToSerialize) {
} else {
try {
prm_objJsonGenerator.writeStartObject();
prm_objJsonGenerator.writeNumberField("applicationId", prm_objObjectToSerialize.getLoanApplicationId());
prm_objJsonGenerator.writeStringField("status", prm_objObjectToSerialize.getStatus());
prm_objJsonGenerator.writeNumberField("amount", prm_objObjectToSerialize.getAmount());
prm_objJsonGenerator.writeNumberField("startdate", prm_objObjectToSerialize.getStartDate().getTime());
prm_objJsonGenerator.writeNumberField("duration", prm_objObjectToSerialize.getDuration());
prm_objJsonGenerator.writeStringField("businesstype", prm_objObjectToSerialize.getBusinessType());
prm_objJsonGenerator.writeStringField("currency", prm_objObjectToSerialize.getCurrency());
prm_objJsonGenerator.writeStringField("productcategory", prm_objObjectToSerialize.getProductCategory());
prm_objJsonGenerator.writeStringField("purpose", prm_objObjectToSerialize.getPurpose());
prm_objJsonGenerator.writeStringField("mystory", prm_objObjectToSerialize.getMyStory());
prm_objJsonGenerator.writeStringField("salesRepresentative", prm_objObjectToSerialize.getSalesRepresentative());
} catch (Exception v_exException) {
//ExceptionController.getInstance().error("Error while Serializing the Loan Application Object", v_exException);
} finally {
prm_objJsonGenerator.writeEndObject();
}
}
}
}
Hope This may help u alot. Thanks..
You can do it by annotating your class with #JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
Example:
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public myClass{
// attributes and accessors
}
You can find some useful informations at Jackson faster xml