I am receiving following error message, I have Status class but it is not being recognized. I've got no idea how to proceed and could not find an answer online.
Error
org.springframework.http.converter.HttpMessageNotReadableException: Could
not read JSON: Unrecognized field "Status" (class
com.myproject.ticket.EventsResponse), not marked as ignorable (3 known
properties: "events", "status", "page"])
....
Caused by:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "Status" (class com.myproject.ticket.EventsResponse),
not marked as ignorable (3 known properties: "events", "status", "page"])
EventsResponse
#XmlRootElement(name = "EventsResponse")
#XmlAccessorType(XmlAccessType.FIELD)
public class EventsResponse {
#XmlElement(name = "Status")
private Status status;
#XmlElement(name = "Paging")
private Page page;
#XmlElementWrapper(name="Events")
#XmlElement(name = "Event")
private List<Event> events;
.....
Status
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Status {
#XmlElement(name = "Version")
private double version;
#XmlElement(name = "TimeStampUtc")
private Date timeStampUtc;
#XmlElement(name = "Code")
private int code;
#XmlElement(name = "Message")
private String message;
#XmlElement(name = "Details")
private String details;
Response
<EventsResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Status>
<Version>2.0</Version>
<TimeStampUtc>2016-06-11T09:32:21</TimeStampUtc>
<Code>0</Code>
<Message>Success</Message>
<Details />
</Status>
<Paging>
<PageNumber>1</PageNumber>
<PageSize>50</PageSize>
<PageResultCount>15</PageResultCount>
<TotalResultCount>15</TotalResultCount>
<TotalPageCount>1</TotalPageCount>
</Paging>
<Events>
<Event>
I added following to Status but I am still receiving the same error.
#XmlElement(name = "Status")
#JacksonXmlProperty(localName = "Status")
private Status status;
I failed to reconstruct your issue.
I created a test project github here that has Jackson configuration and JAXB annotations that meet your needs.
I added dependencies to jackson-dataformat-xml and woodstox-core-asl as your Stax implementations (in my test project I am using Jackson 2.6.6. ,Spring 4.2.6)
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
Configure the Jackson2ObjectMapperBuilder to use both Jackson and JAXB annotations. This is a Spring-boot example to convert to Simple Spring-MVC look here
#SpringBootApplication
public class EventAppConfiguration {
public static void main(String[] args) {
SpringApplication.run(EventAppConfiguration.class, args);
}
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
b.indentOutput(true)
//Enable Introspects for both Jackson and JAXB annotation
.annotationIntrospector(introspector())
//Use CamelCase naming
.propertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss"));
return b;
}
#Bean
public AnnotationIntrospector introspector(){
AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);
return pair;
}
}
Note the use of
PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE
it will save you the need to specify alternative naming for first letter capitalization and will require the use for JAXB annotation only for warping and renaming for example my EventsResponse will look like:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class EventsResponse {
private Status status;
#XmlElement(name = "Paging")
private Page page;
#XmlElementWrapper(name = "Events")
#XmlElement(name = "Event")
private List<Event> events;
...
}
You have two options, assuming you are using Jackson to deserialize your XML objects. The simplest is to use Jackson's own XML annotations instead of or as well as the JAXB #XmlElement annotations. For example:
#XmlElement(name = "Status")
#JacksonXmlProperty(localName = "Status")
private Status status;
(The #XmlElement annotation is in the jackson-dataformat-xml package in Maven - the version should match your other Jackson package versions.)
The alternative is to register an AnnotationIntrospector as part of your deserialization chain - ie. (from a unit test):
XmlMapper mapper = new XmlMapper();
AnnotationIntrospector aiJaxb = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
mapper.setAnnotationIntrospector(aiJaxb);
// EVENTS_RESPONSE is the incoming XML
EventsResponse response = mapper.readValue(EVENTS_RESPONSE, EventsResponse.class);
This recognises the #XmlElement annotation. There are more details in this answer if you need to include this as part of a Spring configuration, for example.
(In order to use the JaxbAnnotationIntrospector class, you will need the jackson-module-jaxb-annotation module from Maven.)
Related
I have problem with json deserialization on consumer side. When I try to consume message I am getting
org.springframework.kafka.support.serializer.DeserializationException: failed to deserialize; nested exception is java.lang.IllegalStateException: No type information in headers and no default type provided
In order to prevent TypeId header sending/processing I've added both spring.json.add.type.headers=false and spring.json.use.type.headers=false properties but nothing changed.
I have an example of my classes that I send through kafka:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
public abstract class TaskDto {
#NotNull
private final UUID taskId;
}
#Data
#ToString(callSuper = true)
#Jacksonized
#SuperBuilder
#EqualsAndHashCode(callSuper = true)
public class SimpleTaskDto extends TaskDto {
#NotNull
private final Map<String, Object> data;
}
I expect that json deserialization should use #class property in order to determine the actual class, but it looks like I am missing something important here. Should I use spring.kafka.consumer.properties.spring.json.value.type.method property to define custom type determination method?
You must configure an ObjectMapper respectively:
StdTypeResolverBuilder typer = new TypeResolverBuilder(DefaultTyping.EVERYTHING,
mapper.getPolymorphicTypeValidator());
typer = typer.init(JsonTypeInfo.Id.CLASS, null);
typer = typer.inclusion(JsonTypeInfo.As.PROPERTY);
mapper.setDefaultTyping(typer);
I am using JHipster(spring boot) to generate my project. I would like to hide/show fields in JSON from application.yml. for exemple:
I have the following class
#Entity
#Table(name = "port")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Port implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#Column(name = "id")
private Long id;
#Column(name = "city")
private String city;
#Column(name = "description")
private String description;
//getters & setters
}
My GET method return a response like:
{
"id": 1,
"city": "boston",
"description": "test test"
}
I would like to be able to include/exclude some fields from application.yml (since i don't have application.properties) otherwise to have something like:
//application.yml
include: ['city']
exclude: ['description']
in this exemple my json should look like:
{
"id": 1,
"city": "boston",
}
for exemple if I have 40 fields and I need to hide 10 and show 30 I just want to put the 10 I want to hide in exclude in application.yml without go everytime to change the code. I guess #jsonignore hide fields but I don't know how to do it from application.yml
Sorry for not explaining well. I hope it's clear.
Thank you in advance for any suggestion or solution to do something similar
Spring boot by default uses Jackson JSON library to serialize your classes to Json. In that library there is an annotation #JsonIgnore which is used precisely for the purpose to tell Json engine to egnore a particular property from serialization/de-serialization. So, lets say in your entity Port you would want to exclude property city from showing. All you have to do is to annotate that property (or its getter method) with #JsonIgnore annotation:
#Column(name = "city")
#JsonIgnore
private String city;
You can try to create a hashmap in your controller to manage your HTTP response.
Map<String, Object> map = new HashMap<>();
map.put("id", Port.getId());
map.put("city", Port.getCity());
return map;
Basically you don't expose your Port entity in your REST controller, you expose a DTO (Data Transfer Object) that you value from your entity in service layer using a simple class (e.g PortMapper). PortDTO could also be a Map as suggested in other answer.
Your service layer can then use a configuration object (e.g. PortMapperConfiguration) that is valued from application.yml and used by PortMapper to conditionally call PortDTO setters from Port getters.
#ConfigurationProperties(prefix = "mapper", ignoreUnknownFields = false)
public class PortMapperConfiguration {
private List<String> include;
private List<String> exclude;
// getters/setters
}
#Service
public class PortMapper {
private PortMapperConfiguration configuration;
public PortMapper(PortMapperConfiguration configuration) {
this.configuration = configuration;
}
public PortDTO toDto(Port port) {
PortDTO dto = new PortDTO();
// Fill DTO based on configuration
return dto;
}
}
I am working on an e-policy project where i need to save different types of policies. For simplicity i am considering only two types "LifeInsurance" and "AutoInsurance". What i want to achieve is if the JSON request to create policy contains "type":"AUTO_INSURANCE" then the request should be mapped to AutoInsurance.class likewise for LifeInsurance but currently in spring boot app the request is getting mapped to parent class Policy eliminating the specific request fields for auto/Life insurance. The domain model i have created is as below.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#NoArgsConstructor
#Getter
#Setter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include =
JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({ #Type(value = AutoInsurance.class, name = "AUTO_INSURANCE"),
#Type(value = LifeInsurance.class) })
public class Policy {
#Id
#GeneratedValue
private Long id;
private String policyNumber;
#Enumerated(EnumType.STRING)
private PolicyType policyType;
private String name;
}
My AutoInsurance class is below.
#Entity
#NoArgsConstructor
#Getter
#Setter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonTypeName(value = "AUTO_INSURANCE")
public class AutoInsurance extends Policy {
#Id
#GeneratedValue
private Long id;
private String vehicleNumber;
private String model;
private String vehicleType;
private String vehicleName;
}
Below is LifeInsurance type child class
#Entity
#NoArgsConstructor
#Getter
#Setter
#JsonTypeName(value = "LIFE_INSURANCE")
public class LifeInsurance extends Policy {
#OneToMany(mappedBy = "policy")
private List<Dependents> dependents;
private String medicalIssues;
private String medication;
private String treatments;
}
To save the policy details, I am sending JSON request from UI with a "type" property indicating the type of insurance in the request.
When i run the below test method, JSON request gets mapped to the correct child class as required.
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper map = new ObjectMapper();
String s = "{\"id\": 1,\"policyNumber\": \"Aut-123\",\"type\": \"AUTO_INSURANCE\",\"policyType\": \"AUTO_INSURANCE\",\"name\": null,\"address\": null,\"contact\": null,\"agentNumber\": null,\"agentName\": null,\"issuedOn\": null,\"startDate\": null,\"endDate\": null,\"vehicleNumber\": \"HR\",\"model\": null,\"vehicleType\": \"SUV\",\"vehicleName\": null}";
Policy p = map.readValue(s, Policy.class);
System.out.println(p.getClass());
//SpringApplication.run(EPolicyApplication.class, args);
}
But when i run the same in Spring boot in a RESTController postmapping, I am getting a PArent class object instead of the child class object.
#RestController
#RequestMapping("/policy")
public class PolicyController {
#PostMapping
public void savePolicy(Policy policy) {
System.out.println(policy.getClass());
}
}
I can get the JSON as string, autowire objectmapper and parse manually but i want to understand if its a known issue and if anyone else has faced the same with Spring boot. I have searched for solutions on this but i got was solution to deserializing to polymorphic classes but nothing related to issue with Spring boot.
In your method you haven't annotated the Policy method argument with #RequestBody. Which leads to Spring creating just an instance of Policy instead of using Jackson to convert the request body.
#PostMapping
public void savePolicy(#RequestBody Policy policy) {
System.out.println(policy.getClass());
}
Adding the #RequestBody will make that Spring uses Jackson to deserialize the request body and with that your annotations/configuration will be effective.
I have some XML that I want to turn into an object using Jackson FasterXML. The XML looks like this:
<services>
<service id="1" name="test">
<addresses>
<postalAddress id="2" line1="123 Fake Street" city="Springfield" />
</addresses>
</service>
</services>
I am deserializing this as an object successfully with these classes:
JsonIgnoreProperties(ignoreUnknown = true)
#JacksonXmlRootElement(localName = "services")
public class ServiceWrapper {
#JacksonXmlProperty(localName = "service")
private Service service;
//Getters and setters [...]
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class Service {
#JacksonXmlProperty(isAttribute = true)
private int id;
#JacksonXmlProperty(isAttribute = true)
private String name;
#JacksonXmlProperty(localName = "addresses")
private AddressWrapper addresses;
//Getters and setters [...]
}
public class AddressWrapper {
#JacksonXmlProperty(localName = "postalAddress")
private List<Address> addresses;
//Getters and setters [...]
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class Address {
#JacksonXmlProperty(isAttribute = true, localName = "id")
private int id;
#JacksonXmlProperty(isAttribute = true, localName = "line1")
private int address1;
#JacksonXmlProperty(isAttribute = true, localName = "city")
private int city;
//Getters and setters [...]
}
And the code to do the mapping:
JacksonXmlModule module = new JacksonXmlModule();
module.setDefaultUseWrapper(false);
ObjectMapper mapper = new XmlMapper(module);
mapper.registerModule(new JSR310Module());
mapper.configure(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, true);
ServiceWrapper serviceWrapper = mapper.readValue(xmlString, ServiceWrapper.class);
return serviceWrapper.getService();
This all works fine, but it's a lot of overhead and ugly code to have the ServiceWrapper and AddressWrapper classes; when really all I want is the data in the <service> node and <postalAddress> node. Is it possible to tell the Jackson object to directly populate a list of Addresses in my Service class without having the AddressWrapper class to represent the <addresses> node? Similarly to take the entire xml and populate a Service class directly without needing a ServiceWrapper class?
The normal way to avoid writing/maintaining such code is to use JAXB to generate the Java code (with appropriate annotations). This process uses an XML schema (.xsd) as the input, with an optional bindings file (.xjb) to customize the generated code.
It looks like many of the JAXB annotations are supported by Jackson.
I will also note that JAXB code generator (xjc) supports plugins that allow you to do pretty much anything you want to augment the generated code (e.g., with additional methods or annotations).
I have a problem with Jersey (2.13). I suppose that the problem related to un-marshalling mechanism. My User entity has a field - collection of Role entities. When I try to get it, I get an error.
Here is my classes
#XmlRootElement(name = "user")
public class User {
private Set<Role> roleSet;
// other fields omitted
#XmlElementWrapper(name = "roles")
#XmlElement(name = "role")
public Set<Role> getRoleSet() { /* compiled code */ }
// all other getters with simple JAXB annotations #XmlElement omitted
}
Role
#XmlRootElement(name = "role")
public class ClientRole {
private String name;
private boolean active;
private String id;
#XmlElement(name = "name")
public String getName() { /* compiled code */ }
// all getters with JAXB annotations omitted
}
And my Jersey client code
HttpAuthenticationFeature authenticationFeature = HttpAuthenticationFeature.basic("freddy", "qwerty");
Client client = ClientBuilder
.newClient()
.register(authenticationFeature)
.register(JacksonFeature.class);
WebTarget target = client.target("http://localhost:4444/server/");
Response resp = target
.path("/api/users/freddy")
.request()
.accept(MediaType.APPLICATION_JSON)
.get();
User entity = resp.readEntity(User.class);
System.out.println(entity);
How can I fix that error? Or maybe I just forgot to register some new feature (for the new Jersey release 2.13)?
Exception in thread "main" javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
at com.test.CreateUserExample.main(CreateUserExample.java:36)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "roles" (class com.server.dto.auth.User), not marked as ignorable (6 known properties: "enabled", "statusId", "role", "username", "emailAddress", "password"])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#34cdeda2; line: 1, column: 141] (through reference chain: com.server.dto.auth.User["roles"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:771)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
UPDATE
Here is my dependencies
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.13</version>
</dependency>
UPDATE 2
String entity = resp.readEntity(String.class);
{"name":"Bob","enabled":true,"roles":[{"name":"ADMIN","active":false,"id":12313214},{"name":"USER","active":true,"id":45353}]}
The problem is that while serializing, you're probably using MOXy which adds the additional wrapping element (roles in this case) which is not fully supported by Jackson.
You can either use MOXy for de-serialization at the client side
Client client = ClientBuilder
.newClient()
.register(authenticationFeature)
.register(MoxyJsonFeature.class);
or to slightly change your model class so that JAXB serializer (MOXy) produces something that Jackson is able to de-serialize
#XmlElement(name = "roles")
public Set<Role> getRoleSet() {
return roleSet;
}
Finally, you can let Jersey use Jackson on the server side by registering JacksonFeature:
#ApplicationPath("/")
public class TestApplication extends ResourceConfig {
public TestApplication() {
register(TestResource.class);
register(JacksonFeature.class);
}
}