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
Related
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
So I am just trying out Jersey for REST services and it seems to we working out fine. I only expose get services and all of the object types that I expose with these services have an immutable object representation in Java. By default Jersey seems to use a parser (JAXB?), requiring a #XmlRootElement annotation for the class that should be parsed, zero-arg constructor and setters.
I have been using Gson with no zero-arg constructor, no setters and final on all fields with no problems at all. Is there any way to accomplish this with Jersey(i.e. the paser it is using)? I have seen solutions with adapter classes that map data from a immutable object to a mutable representation, but this seems like a lot of boilerplate(new classes, more annotations, etc.) if it can be achieved with Gson without anything added.
Note: 1) I have heard people promote using zero-arg constructor and claim that Gson should not work without it. This is not what I am interested in. 2) I really have tried googling this but my keywords might be off. In other words, humiliate me in moderation.
EDIT 1:
My webservice works if I do like this:
#XmlRootElement
public class Code{
private String code; //Silly object just used for example.
public Code(){}
//(G || S)etters
}
With this class exposing the object:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Set<Code> get(#QueryParam("name") String name) { // Here I want to use a class of my own instead of String name, haven't figured out how yet.
return this.codeService.get(name);
}
If I replace the Code with the following, the webservice stops working:
public class Code{
private final String code;
#JsonCreator
public Code(#JsonProperty("code") String code) {
this.code = code;
}
//Getters omitted
}
What I want is to be able to 1) have immutable objects that can be parsed to/from json and 2) Be able to define something like #RequestBody in Spring MVC for my incoming objects.
Actually this could be pretty easy with Genson. You just need the jar and then configure the Genson feature to use constructors with arguments (if you don't want to put annotations on it).
Genson genson = new GensonBuilder().useConstructorWithArguments(true).create();
// and then register it with jersey
new ResourceConfig().register(new GensonJaxRSFeature().use(genson));
Or you can use JsonProperty on the arguments. See the User Guide for more details.
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;
I'm using the latest snapshot spring-data-rest-webmvc:2.0.0-SNAPSHOT.
When Jackson tries to serialize PersistentEntityResource to JSON, it goes into an infinite loop on the "persistentEntity" property. I understand the property is supposed to be ignored, but it's not.
public class PersistentEntityResource<T> extends Resource<T> {
#JsonIgnore private final PersistentEntity<?, ?> entity;
...
public PersistentEntity<?, ?> getPersistentEntity() {
return entity;
}
}
Maybe Jackson sees the getter that doesn't match the name of the field and decides to serialize? Or do I have some configuration wrong?
This seems to be an error reported here:
https://jira.springsource.org/browse/DATAREST-117?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
I solved the problem by making sure that I don't have another bean in the spring context called "objectMapper". It was a default Jackson ObjectMapper that was overwriting the custom one configured in RepositoryRestMvcConfiguration.
Long story short, one of my entities has a GeometryCollection that throws an exception when you call "getBoundary" (the why of this is another book, for now let's say this is the way it works).
Is there a way I can tell Jackson not to include that specific getter? I know I can use #JacksonIgnore when I do own/control the code. But this is not case, jackson ends reaching this point through continuous serialization of the parent objects. I saw a filtering option in jackson documentation. Is that a plausible solution?
Thanks!
You can use Jackson Mixins. For example:
class YourClass {
public int ignoreThis() { return 0; }
}
With this Mixin
abstract class MixIn {
#JsonIgnore abstract int ignoreThis(); // we don't need it!
}
With this:
objectMapper.getSerializationConfig().addMixInAnnotations(YourClass.class, MixIn.class);
Edit:
Thanks to the comments, with Jackson 2.5+, the API has changed and should be called with objectMapper.addMixIn(Class<?> target, Class<?> mixinSource)
One other possibility is, if you want to ignore all unknown properties, you can configure the mapper as follows:
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Using Java Class
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
Using Annotation
#JsonIgnoreProperties(ignoreUnknown=true)
Annotation based approach is better. But sometimes manual operation is needed. For this purpose you can use without method of ObjectWriter.
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
ObjectWriter writer = mapper.writer().withoutAttribute("property1").withoutAttribute("property2");
String jsonText = writer.writeValueAsString(sourceObject);
Mix-in annotations work pretty well here as already mentioned. Another possibility beyond per-property #JsonIgnore is to use #JsonIgnoreType if you have a type that should never be included (i.e. if all instances of GeometryCollection properties should be ignored). You can then either add it directly (if you control the type), or using mix-in, like:
#JsonIgnoreType abstract class MixIn { }
// and then register mix-in, either via SerializationConfig, or by using SimpleModule
This can be more convenient if you have lots of classes that all have a single 'IgnoredType getContext()' accessor or so (which is the case for many frameworks)
I had a similar issue, but it was related to Hibernate's bi-directional relationships. I wanted to show one side of the relationship and programmatically ignore the other, depending on what view I was dealing with. If you can't do that, you end up with nasty StackOverflowExceptions. For instance, if I had these objects
public class A{
Long id;
String name;
List<B> children;
}
public class B{
Long id;
A parent;
}
I would want to programmatically ignore the parent field in B if I were looking at A, and ignore the children field in A if I were looking at B.
I started off using mixins to do this, but that very quickly becomes horrible; you have so many useless classes laying around that exist solely to format data. I ended up writing my own serializer to handle this in a cleaner way: https://github.com/monitorjbl/json-view.
It allows you programmatically specify what fields to ignore:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(JsonView.class, new JsonViewSerializer());
mapper.registerModule(module);
List<A> list = getListOfA();
String json = mapper.writeValueAsString(JsonView.with(list)
.onClass(B.class, match()
.exclude("parent")));
It also lets you easily specify very simplified views through wildcard matchers:
String json = mapper.writeValueAsString(JsonView.with(list)
.onClass(A.class, match()
.exclude("*")
.include("id", "name")));
In my original case, the need for simple views like this was to show the bare minimum about the parent/child, but it also became useful for our role-based security. Less privileged views of objects needed to return less information about the object.
All of this comes from the serializer, but I was using Spring MVC in my app. To get it to properly handle these cases, I wrote an integration that you can drop in to existing Spring controller classes:
#Controller
public class JsonController {
private JsonResult json = JsonResult.instance();
#Autowired
private TestObjectService service;
#RequestMapping(method = RequestMethod.GET, value = "/bean")
#ResponseBody
public List<TestObject> getTestObject() {
List<TestObject> list = service.list();
return json.use(JsonView.with(list)
.onClass(TestObject.class, Match.match()
.exclude("int1")
.include("ignoredDirect")))
.returnValue();
}
}
Both are available on Maven Central. I hope it helps someone else out there, this is a particularly ugly problem with Jackson that didn't have a good solution for my case.
If you want to ALWAYS exclude certain properties for any class, you could use setMixInResolver method:
#JsonIgnoreProperties({"id", "index", "version"})
abstract class MixIn {
}
mapper.setMixInResolver(new ClassIntrospector.MixInResolver(){
#Override
public Class<?> findMixInClassFor(Class<?> cls) {
return MixIn.class;
}
#Override
public ClassIntrospector.MixInResolver copy() {
return this;
}
});
One more good point here is to use #JsonFilter.
Some details here Feature: JSON Filter