I used the RestyGWT's JsonEncoderDecoder interface to encode/decode some objects. Among them there are instances of classes having properties not exposed using getter/setter methods. I tried annotating corresponding properties with org.codehaus.jackson.annotate.JsonProperty. But it's not working, causing error
[ERROR] [jsonsample] - field must not be private: com.mycompany.jsonsample.ItemList.items
com.mycompany.jsonsample.ItemList is the class with property items which has no getter/setter and annotated as said above.
Also is it possible to tell the encoder/decoder to skip some properties?
Example with private field and annotated constructor, you should provide more info on your problem though.
public abstract class Parent
{
#JsonCreator
public Parent(#JsonProperty("name") String name)
{
this.name = name;
}
#Override
public String getName()
{
return name;
}
private String name;
}
Related
Is is possible to exclude JsonProperties in the output of a Spring Boot Rest call based on a defined condition? (eg. the role of the user)
Example:
public class Employee{
#JsonProperty
private String name;
#JsonProperty
private String fieldForManagerOnly;
#JsonProperty
private String fieldForEmployeeOnly;
}
I want to have the fieldForManagerOnly only serialized in the JSON output when the user has the ROLE manager.
I've already tried the solution with the #JsonView (as described in Latest Jackson integration improvements in Spring) but that solution is very limited as the #JsonView is bound to one controler method and I want to have only one controller method.
I've solved the problem myself. I used the JsonView solution but instead of an annotation I select the JsonView from code.
First you need an interface for the Views.
public class JsonViews {
public interface EmployeeView {}
public interface ManagerView {}
}
Mark the fields in the Model class with the #JsonView annotations.
public class Employee{
#JsonProperty
private String name;
#JsonView(JsonViews.ManagerView.class)
private String fieldForManagerOnly;
#JsonView(JsonViews.EmployeeView.class)
private String fieldForEmployeeOnly;
}
In your controller set the JsonView to use based on the role (or some other condition):
#RequestMapping(value = "/{employeeId}", method = RequestMethod.GET)
public ResponseEntity<MappingJacksonValue> getEmployee(#PathVariable long employeeId) {
Employee employee = employeeService.getEmployee(employeeId);
MappingJacksonValue jacksonValue = new MappingJacksonValue(employeeResourceAssembler.toResource(employee));
if (getRole().equals("MANAGER")) {
jacksonValue.setSerializationView(JsonViews.ManagerView.class);
} else if (getRole().equals("EMPLOYEE")) {
jacksonValue.setSerializationView(JsonViews.EmployeeView.class);
}
return new ResponseEntity<>(jacksonValue, HttpStatus.OK);
}
Annotate the field with
#JsonInclude(JsonInclude.Include.NON_NULL)
and make sure to set the field fieldForManagerOnly to null if the current user is not a manager.
According to RestyGWT documentation one must use an abstract super class for this to work, for instance, given:
#JsonSubTypes(#Type(value=PersonImpl.class, name="PersonImpl"))
#JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="#class")
public abstract class Person{
public abstract String getName();
public abstract void setName(String name);
}
#JsonTypeName("PersonImpl")
public class PersonImpl extends Person{
private String name;
#Override
public final String getName() {
return name;
}
#Override
public final void setName(String name) {
this.name = name;
}
}
If I use the defined encoder/decoder this would work:
Person personInstance = new PersonImpl();
personInstance.setName("TestName");
PersonCodec codec = GWT.create(PersonCodec.class);
JSONValue json = codec.encode(personInstance);
Im trying to do something quite similar but with a small difference, that is, instead of Person being an abstract class I want it to be an Interface:
#JsonSubTypes(#Type(value=PersonImpl.class, name="PersonImpl"))
#JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="#class")
public interface Person{
public String getName();
public void setName(String name);
}
For some reason this doesn't seem to work, as soon as I do that I start getting Errors when the JsonEncoderDecoder is generated. Has someone been able to achieve this?
Thanks!
Why not define your interface and make your abstract class implement it?
I am using Autobean framework to encode/decode JSON in my GWT application. It works in cases with the interfaces having getters and setters. But is there any way to do it some other way to do this without specifying a setThisCollectionProperty instead using an addToThisCollectionProperty method?
For example, I have an interface IPerson like this:
public interface IPerson {
public String getName();
public void setName(String name);
public int getAge();
public void setAge(int age);
public List<String> getIds();
public void addId(String id);
}
BeanFactory is like this:
public interface BeanFactory extends AutoBeanFactory {
public AutoBean<IPerson> person();
public AutoBean<IPerson> person(IPerson person);
}
and in Person class which implements IPerson,
public class Person implements IPerson {
private String name;
private List<String> ids;
...
public List<String> getIds() {
return ids;
}
public void addId(String id) {
...
ids.add(id);
}
}
It works if the addId(String id) is replaced with setIds(List<String> ids).
Otherwise the following error is shown:
The com.mycompany.jsonsample.beans.IPerson parameterization is not simple, but the person method does not provide a delegate
Is it possible to encode/decode without a set method?
AutoBean manages all getters and setters, and only getters and setters. For any other method, you have to use a category.
Using a category, you could thus implement addId(…) as getIds().add(…), or possibly directly call addIds on the underlying object if the AutoBean is a wrapper.
I'm making a DB replication mechanism. I just want to send over an object once over the network. I have implemented a custom serializer that makes a reference to the object using an UUID instead of serializing the object itself. On the other side I plan to just look up the UUID in DB in order to create the none serialized object.
The problem I have is that Jackson expects a type id from my custom serializer. How do I create a type id?
I got the follwing class:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="objectType")
public class Brand implements Serializable {
private String uuid;
#JsonSerialize(using = EntitySerializer.class)
private Producer producer = null;
// Omitting getters and setters
}
and
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="objectType")
public class Producer extends Entity implements Serializable {
private String uuid;
private String name;
//Omitting getters and setters.
}
To serialize Producer i got the following serializer:
public class EntitySerializer extends JsonSerializer<Entity> {
#Override
public void serialize(Entity value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeString(value.getUUID());
}
}
When I serialize I get the following error because I don't generate any #JsonTypeInfo type Id.
com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type com.fodolist.model.Producer (through reference chain: com.fodolist.server.callprocessor.SyncContainter["one"]->com.fodolist.model.Brand["producer"])
Is there any better way to solve the problem?
Maybe have a look at #JsonIdentityInfo; one of id generators creates UUIDs to use as ids. By default the first instance will be serialized in its entirety (otherwise it is not possible to deserialize the data), but you can change that by using #JsonIdentityReference to indicate that all instances are to be serialized using just the id (generated, or accessed via property).
In case the uuid field is already set, you can use the PropertyGenerator as follow:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class Producer extends Entity {
....
}
I am using Jackson (2.1.1) for JSON serialization/deserialization. I have an existing class with JAXB annotations. Most of these annotations are correct and can be used as-is with jackson. I am using mix-ins to slightly alter the deserialization/serialization of these classes.
In my ObjectMapper constructor I do the following:
setAnnotationIntrospector(AnnotationIntrospector.pair(
new JacksonAnnotationIntrospector(),
new JaxbAnnotationIntrospector(getTypeFactory())));
Based on the above, the Jackson annotations have precedence over Jaxb, because of the order of the introspectors. This is based on the Jackson Jaxb docs. For fields that I want ignored, adding #JsonIgnore to the field in the mix-in is working fine. There are a couple of fields that are marked as #XmlTransient in the existing classes that I do not want ignored. I have tried add #JsonProperty to the field in the mix-in, but it doesn't seem to work.
Here is the original class:
public class Foo {
#XmlTransient public String getBar() {...}
public String getBaz() {...}
}
Here is the mix-in:
public interface FooMixIn {
#JsonIgnore String getBaz(); //ignore the baz property
#JsonProperty String getBar(); //override #XmlTransient with #JsonProperty
}
Any idea how to resolve this without modifying the original class?
I also tested adding #JsonProperty to the members instead of using mix-ins:
public class Foo {
#JsonProperty #XmlTransient public String getBar() {...}
#JsonIgnore public String getBaz() {...}
}
I seem to get the same behavior as I did with the mix-in. Unless #XmlTransient is removed, the property is ignored.
The issue is that the AnnotationIntrospectorPair.hasIgnoreMarker() method basically ignores the #JsonProperty if either introspector detects an ignore marker:
public boolean hasIgnoreMarker(AnnotatedMember m) {
return _primary.hasIgnoreMarker(m) || _secondary.hasIgnoreMarker(m);
}
ref: github
A workaround is to subclass the JaxbAnnotationIntrospector to get the desired behavior:
public class CustomJaxbAnnotationIntrospector extends JaxbAnnotationIntrospector {
public CustomJaxbAnnotationIntrospector(TypeFactory typeFactory) {
super(typeFactory);
}
#Override
public boolean hasIgnoreMarker(AnnotatedMember m) {
if ( m.hasAnnotation(JsonProperty.class) ) {
return false;
} else {
return super.hasIgnoreMarker(m);
}
}
}
Then just use the CustomJaxbAnnotationIntrospector in the AnnotationIntrospectorPair.