I'm having a JAX-RS web application method which takes a JSON entity and produces one. But I'm having problems with the POJOS matching the consumed JSON entity, specificly with their annotations.
How do I configure the POJOS so I don't need to write the name of the properties in the consumed JSON entity? I would rather have a custom name for them.
Example
Prefered JSON entity:
{
"foo_bar" : "spam"
}
POJO:
#XmlRootElement
public class A {
private String fooBar;
public String getFooBar() {
return fooBar;
public void setFooBar(String fooBar) {
this.fooBar = fooBar;
}
So far I tried the #XmlElement(name="foo_bar") annotation, but it didn't work. Some other stackoverflow thread suggested #JsonProperty("foo_bar"), but what's the difference between them? Additionally, does this annotation work with the existing #Xml annotations?
Related
I am using my REST API, which is camelCase, to call a 3rd party graphQL API, which returns objects in snake_case. To do this, I am using a library to generate the mappers and model files (graphql-java-codegen). As a result, I end up with models that look like
class MyModel {
public my_string;
//...
}
I cannot force the model generation to be done in camelCase. I would like to directly return the generated models as ResponseObjects to my client, but would like the serialization to be in camelCase, without needing to copy the generated model with camelCase fields. So when returning the example, it would look like
{
"myString": "Example str"
}
In my code generation configuration, I have the ability to add annotations at both the class and field level (but no way to customize it at each field, so no #JsonProperty("myString"))
tl;dr:
Is there some annotation / Spring Boot configuration I can use to force models with snake_case naming to serialize into camelCase, without needing to specify the #JsonProperty for every field?
You can create a custom PropertyNamingStrategy subclass and set it as the default naming strategy for one ObjectMapper mapper used only for serialization (you can check this for conversion of a string from snake_case to camelcase):
public class SnakeCaseToCamelCase extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
return Pattern.compile("_([a-z])")
.matcher(defaultName)
.replaceAll(m -> m.group(1).toUpperCase());
}
}
public class MyModel {
public String my_string = "Example str";
}
MyModel model = new MyModel();
mapper.setPropertyNamingStrategy(new SnakeCaseToCamelCase());
//it prints {"myString":"Example str"}
System.out.println(mapper.writeValueAsString(model));
Hi StackOverflow Community,
I am currently trying to deserialize JSON request bodies provided via Spring Boot #RestController.
The request body contains the following array:
{
...
"productIds": [
"123abc",
"234def"
],
...
}
However, I don't want to deserialize the product IDs into a list of Strings, but rather use a simple wrapper class (for various reasons, including but not limited to additional type safety and validation opportunities). Consequently the class looks like this (Lombok annotations were used to keep the code snippet short):
#Value
#AllArgsConstructor
public class TheRequest {
...
List<ProductId> productIds;
...
}
with ProductId being just a simple wrapper as already said (validation annotations are omitted for the sake of brevity):
#Value
#AllArgsConstructor
public class ProductId{
String id;
}
Looking at Stackoverflow I only found ways to achieve this using rather verbose custom deserialization methods.
However, I am a bit astonished, that Jackson does not provide this functionality out of the box. Consequently it would be great if anyone has any idea if
there is a more elegant way to achieve deserialization of a array of Strings into a List of WrapperObjects, ideally only using Jackson annotations?
there is an elegant way to achieve serialization of such a resulting List of ProductId wrapper objects back into String objects, ideally also using only Jackson annotations? I tried Jacksons #Value but that did not provide the required result.
To me still to verbose but it seems to be a working solution with Jacson 2.14+:
public record PayloadId(String id) {
#JsonCreator(mode = Mode.DELEGATING)
public PayloadId{}
#JsonValue
#Override
public String id() {
return id;
}
}
...and here is the records test https://github.com/FasterXML/jackson-databind/blob/2.14/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordCreatorsTest.java
I have a Spring Boot app using Jackson. I'm not using Jersey just regular Spring MVC. I have a Wrapper Request class:
public class WrapperRequest {
#NotNull
private final Object obj; // some object that corresponds with a JSON object
#JsonCreator
public WrapperRequest(#JsonProperty("wrapper") final Object obj) {
this.obj = obj;
}
public Object getObj() {return obj}
}
The JSON for this would look like:
{
"wrapper":{
//Object data
}
}
The #NotNull from javax isn't working as I excepted. The way I want it to work is that if the consumer sends in a JSON that have a typo like:
{
"wrapperr":{
//Object data
}
}
Jackson will not map my wrapper class because the key in the JSON doesnt match the JsonProperty i.e ("wrapper") (so Object will be null and then I will get NPE later on if I tried to interact with Object. Am I using Jackson wrong? i.e Jackson maps things that it knows about and the rest is null or am I not using the #NotNull annotation correctly?
Jackson is not aware of Bean Validation annotations like #NotNull. For the specific case of "null", you can mark the constructor parameter as #JsonProperty(required = true). More generally, you can use #NotNull on a property as you did and mark your MVC controller parameter with #Valid (which will not cause deserialization to fail but will cause Spring MVC to return a 400 if the validation fails).
Note that you may also be interested in the UNWRAP_ROOT_VALUE feature, which would allow you to eliminate the need for the wrapper class in this particular case.
I have the following situation
public class MyCustomForm {
private MyCustomType a;
private MyCustomType b;
}
#RestController
public class AController {
#RequestMapping(...)
public void myMethod(#RequestBody MyCustomForm form){
...
}
}
I want to send in a POST request the necessary data to fill the form. The problem is that MyCustomType is a complex data type and cannot be deserialized from JSON.
The first thing I tried was to write a PropertyEditor so that Spring will know how the make the deserialization from a string. This solution works if I use anything else beside #RequestBody (it works with #PathVariable for example).
I made some research and the reason why #RequestBody is not working is because this annotation generates a proxy which uses its own deserialization rules. Those rules do not interfere with custom PropertyEditors.
The next thing I tried was to use a custom Converter. This solution still didn't solved the issue.
Any other ideas?
I understood that the newest version of jackson (version 2) will know about the custom Converters or PropertyEditors but updating my jackson mapper is not really a solution in my case.
You can use #JsonDeserialize for your MyCustomType classes like
public class MyCustomForm {
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType a;
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType b;
}
Some references:
https://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
http://www.davismol.net/2015/06/05/jackson-using-jsonserialize-or-jsondeserialize-annotation-to-register-a-custom-serializer-or-deserializer/
http://www.baeldung.com/jackson-custom-serialization
I'm using spring 4.0.5.RELEASE and jackson-databind 2.2.3 in my web application.
When sending this JSON:
{"keyField1":"57579","keyField2":"sdf","someField":"sdasd","parameters":[{"parameterName":"dfgdfg","parameterValue":"sdf"},{"parameterName":"erwer","parameterValue":"sdfsdf"}]}
to the controller all I get is a HTTP 400 Bad Request at browser,
I don't see any error at local websphere log, but after some tests I saw that the problem is with deserialization of the JSON array to the map.
I never get into the save method at the controller.
Tried some annotation like #JsonDeserialize(as=HashMap.class) without success.
How can I resolve this?
My POJO:
class MyClassId implements Serializable {
private static final long serialVersionUID = 5022985493208399875L;
String keyField1;
String keyField2;
}
#Entity
#IdClass(MyClassId.class)
public class MyClass {
#Id
String keyField1;
#Id
String keyField2;
String someField;
#ElementCollection
#MapKeyColumn(name="parameterName")
#Column(name="parameterValue", length=400)
Map<String, String> parameters;
... Getters and Setters ...
My controller:
#RestController
#RequestMapping("/myclass/**")
public class MyClassController {
#Transactional
#RequestMapping(value = "/save", method = RequestMethod.POST, consumes={"application/json"})
public #ResponseBody ServiceResponce<MyClass> save(#RequestBody MyClass processToSave) {
... Code ...
}
}
I see two solutions to your problem:
You distinguish the entity class that is persistent to the representation class that is sent back to the client. The drawback is that you need to make the representation creation explicitely in your code from the entity
If you use Jackson to serialize your response in the respone (JSON, ...), you can leverage it feature "custom serializer" to adapt the structure of the returned payload according to your needs. See this answer for more details:
Spring 3.2 and Jackson 2 custom object mapper - Spring 3.2 and Jackson 2: add custom object mapper
Register a custom Jackson ObjectMapper using Sprint JavaCconfig - http://magicmonster.com/kb/prg/java/spring/webmvc/jackson_custom.html
Restlet Complex Object to XML serializaton - Restlet Complex Object to XML serializaton
Hope it helps you,
Thierry
In the JSON, parameters is not an object but an array of objects:
"parameters":[{"parameterName":"dfgdfg","parameterValue":"sdf"}, ...
You can not map this on a
Map<String, String> parameters;
Use at least
List<Map<String, String>> parameters;