Map JSON POSTed in Java Spring Rest Controller to POJO - java

I am sending the following JSON object to my Java Spring application
{
"ewsUrl":"https://dummy.url.com",
"ewsIdentityToken":"12345",
"itemIds":["itemids"],
"entityId":null,
"documentType":"Dummy",
"documentStatus":"Record",
"filename":"message.eml",
"metadata":{"title":"message"}
}
I have defined an object public class RequestObject and in my controller I have
public RequestObject
testMyStuff(#CurrentUser User currentUser,
#RequestBody RequestObject myDummyObject) throws Exception {
return myDummyObject
}
My application returns the error Could not read document: Root name 'ewsUrl' does not match expected ('RequestObject') for type...etc
However if I send the JSON formatted like this it successfully maps the object:
{ "RequestObject":
{
"ewsUrl":"https://dummy.url.com",
"ewsIdentityToken":"12345",
"itemIds":["itemids"],
"entityId":null,
"documentType":"Dummy",
"documentStatus":"Record",
"filename":"message.eml",
"metadata":{"title":"message"}
}
}
I do not want to name the object in my JSON, I want to send as described in the first example. How do I achieve this?
UPDATE:
RequestObject.java
public class RequestObject {
public String ewsUrl;
public String ewsIdentityToken;
public String[] itemIds;
public String entityId;
public String documentType;
public String documentStatus;
public String filename;
public Metadata metadata;
public RequestObject() {
}
public static class Metadata {
public String title;
}
}
UPDATE2:
The way it is described in this example suggests that the object does not need to be named in the JSON data in the POST request. I think I am emulating this example, but I'm getting different results.
Is there a configuration for Jackson/Spring that I am missing?
Update 3:
The complete error message is:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document:
Root name 'ewsUrl' does not match expected ('RequestObject') for type
[simple type, class uk.co.test.RequestObject] at [Source:
java.io.PushbackInputStream#7e223182; line: 2, column: 9];
nested exception is com.fasterxml.jackson.databind.JsonMappingException:
Root name 'ewsUrl' does not match expected ('RequestObject') for type
[simple type, class uk.co.test.RequestObject]

There's some configuration settings that look like they can be defined for the ObjectMapper that controls the behaviour of the root element:
https://fasterxml.github.io/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/DeserializationFeature.html#UNWRAP_ROOT_VALUE - see SerializationFeature.html#WRAP_ROOT_VALUE as well.
UNWRAP_ROOT_MODULE is disabled by default according to the docs so not sure why you're seeing the behaviour you are.
Config example for spring is available at
http://docs.spring.io/spring-framework/docs/3.2.3.RELEASE/javadoc-api/org/springframework/http/converter/json/JacksonObjectMapperFactoryBean.html

Just use JSONArray instead of JSONObject
Update
You can get your Json Object via JSONArray.getJSONObject()

Related

Spring Boot: Problems deserializing JSON objects. Change property names

I'm developing a back-end web application using Spring Boot with Java and I have the following problem:
A REST service returns me the following JSON:
{
"cap":"98888"
}
This is my JAVA class which models the output based on the content:
#NoArgsConstructor
#Data
public class MyObject {
private String cap;
}
I would like to change the field name for MY service to return the following JSON:
{
"CAP":"98888"
}
In my JAVA code, I make the call via RestTemplate, like this:
return restTemplate.postForObject(uriBuilder.build().toUri(), new HttpEntity<>(request, headers), MyObject.class);
I've tried to use tons of stuff with Jackson, including #JsonProperty, like this:
#NoArgsConstructor
#Data
public class MyObject {
#JsonProperty("CAP")
private String cap;
}
But the result is this:
{
"CAP":null
}
As if it no longer matches the original name of the property. In practice I can only get an output with correct value but original field name (WHICH I DON'T WANT) or an output with the name of the field I want (i.e. CAP) but with a null value.
Which is the right way to rename properties with Spring annotations?

Fasterxml Jackson databind MismatchedInputException

I am using com.fasterxml.jackson.databind in a spring boot application. When I send a request to my endpoint I receive the following exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of
org.pacakge.domain.controller.Mycontroller (although at least one Creator exists): cannot
deserialize from Object value (no delegate- or property-based Creator)\n at
[Source: (PushbackInputStream); line: 2, column: 3] Is the body of the request formatted correctly?
My controller processes a request body that has the following structure:
{
"portalId": 123,
"objectType": "TYPE",
"objectTypeId": "0-3",
"objectId": 123,
"properties": { ... }
}
The only property that I need is objectId. I've constructed a class to process this object like so:
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
}
I've designed a controller that looks like this
#Slf4j
#RestController
#RequestMapping(path = "/entrypoint")
public class MyController {
#Autowired
public MyController(){}
/**
* REST endpoint handles MyObject
*/
#PostMapping(value = "/endpoint")
public void handleRequest(
#Valid #RequestBody MyObject command
) {
log.debug(command.getDealId());
}
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
}
}
What is interesting about this problem is that my request is processed just fine if I change MyObject to the following structure:
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
private final JSONObject properties; // TODO we shouldn't need this. Fix.
}
I cannot seem to figure out what the issue is. I would love some help on this problem. Maybe there is annotation that I am missing? I am hoping someone else has experienced this issue. I haven't found any information on it by just searching the web.
I added the following line to lombok.config in the root directory of the project:
lombok.anyConstructor.addConstructorProperties=true
And after that managed to deserialize your JSON using this DTO using #JsonIgnoreProperties annotation:
#Value
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyObject {
#JsonProperty("objectId")
String dealId;
}

Jackson conflicting property and getter definitions

I'm extending following third-party class which I can't change:
public class Page {
#JsonProperty("content")
private String content;
public String getContent() {};
}
My implementation of Page looks like this:
public class MyPage extends Page {
#JsonProperty("my-content")
public String getContent() {return super.getContent()};
}
When I'm trying to serialize instance of MyPage class I get the following exception:
java.lang.IllegalStateException: Conflicting property name definitions:
'content' (for [field com.test.Page#content])
vs
'my-content' (for [method com.test.MyPage#getContent(0 params)])
Is there an easy way to force serializer to produce 'my-content' property?
I guess the issue was solved in Jackson 2.4.0. Please check https://github.com/FasterXML/jackson-databind/issues/193.
Try to update your Jackson library to 2.4.0 or above.

Bean Validation Messages from the Program

I have a bean with validation annotations. I am going to trigger the validation manually using the following code:
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, validationGroup);
My question is, 1.) how do you get the field that failed validation and 2.) How to get the associated message?
I do not want to use .properties file. There is no front-end to display. But its a service and I need to send response with failed validation message and field that failed validation.
How do you get the field that failed validation?
The field that failed validation will be returned in the MethodConstraintViolationException. You retrieve the individual violations by calling getConstraintViolations() and then the field can be retrieved by getPropertyPath() and walking the nodes.
However, if you have a case where the field name returned in the response does not match the name of the property in the bean, for example if you are returning snake case responses (i.e. user_name), but your bean property name is username, you have to get a little more creative.
In this scenario you can store the name of the field as a Payload on the bean validation annotation.
Response Field:
#JsonProperty("user_name")
#NotEmpty(message = ErrorMessageKeys.USERNAME_REQUIRED,
payload = {FieldNamePayload.UserName.class})
private String username;
Payload Class:
public class FieldNamePayload
{
/**
* Represents the field name "user_name"
*/
public static class UserName implements ValuePayload
{
private static final String value = "user_name";
#Override
public String getValue()
{
return value;
}
}
}
Retrieving the Payload in your Exception Mapper:
List<MethodConstraintViolation<?>> violations = new ArrayList<MethodConstraintViolation<?>>(exception.getConstraintViolations());
for(MethodConstraintViolation<?> violation : violations)
{
String field = getFieldName(violation);
String message = violation.getMessage();
for(Class<? extends Payload> payload : new ArrayList<Class<? extends Payload>>(violation.getConstraintDescriptor().getPayload()))
{
//Retrieve field name from constraint payload
if(payload.isAssignableFrom(FieldNamePayload.UserName.class))
{
field = getPayloadValue(payload);
}
}
//Create an error response here!
}
How do you get the associated message?
By default the bean validation framework looks up the messages in localized files at the root of the classpath with the following naming convention:
ValidationMessages.properties for the default locale.
ValidationMessages_{locale}.properties when localization is required
If you want to override this behavior and change where the validation messages are being retrieved from you can use hibernate's validation provider and implement a custom ResourceBundleLocator which you would add to the ResourceBundleMessageInterpolator.
ValidatorFactory validatorFactory = Validation
.byProvider(HibernateValidator.class)
.configure()
.messageInterpolator(
new ResourceBundleMessageInterpolator(
new MyCustomResourceBundleLocator()))
.buildValidatorFactory();
Regarding 1, what do you mean with field? The java.lang.reflect.Field? You don't get access to it directly. What you can do is to call ConstraintViolation#getPropertyPath to get the property path. You need to iterate the nodes and at the leaf node you can call for example getName() to get the property name and getKind() to determine the type of the node.
Regarding 2, ConstraintViolation#getMessage() gives you the interpolated error message
I am not sure what you mean when you say that you don't want to use a properties file. Bean Validation will per default read the error messages from properties files (either the built-in ones, or the ones you add to your application).

Error in JSON list deserialize

I'm developing a GWT web application with a RESTful web service. The web service's results are deserializing to POJO with Jackson 1.8. It's work fine with simple fields. However, it fails when it try to deserialize a list of POJO. This is the POJO with list to deserialize:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DatosIndicadoresSVclaveDTO implements Serializable {
...
#XmlAttribute
#JsonDeserialize(contentAs = IdeologicoVOXDTO.class)
public List<IdeologicoVOXDTO> ideologicoVox;
...
//getter/setters
}
And this is the POJO that contains the list
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class IdeologicoVOXDTO implements Serializable {
#XmlAttribute
private Integer numeroPalabra;
#XmlAttribute
private String palabra;
#XmlAttribute
private Integer categoria;
...
//getter/setters
}
The JSON have this structure:
{datosIndicadoresSVclave: {
...
"ideologicoVox":[
{
"categoria":"1",
"numeroPalabra":"1",
"palabra":"abandonado",
...
},
{
"categoria":"2",
"numeroPalabra":"3",
"palabra":"hambre",
...
}
],
...
}
}
When it's running, the web service's results works fine, but the deserialize print this error:
SEVERE: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: java.io.StringReader#10b61ad; line: 1, column: 580] (through reference chain: org.ull.etsii.client.dto.DatosIndicadoresSVclaveDTO["ideologicoVox"])
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:212)
Any idea?? Thanks!!
I had faced some similar kind of problem and tried the following way, and it worked for me.
Create a new class which contains property
~ public List ideologicoVox ~
And use this class reference as property to the main class i.e ~ DatosIndicadoresSVclaveDTO
I've solved!!
The problem is the size list is variable, and it fails if it has one element. The Jackson's version is 1.7, that it can't accept arrays single value. My solution is GSON with a custom register type, and I've used the Joshi's adviced. It works great!! Thanks!!

Categories