I have a class object that contains an interface variable used for callbacks that I don't want serialized into the JSON. I have attempted to use the #JsonIgnoreProperties() annotation to make it ignore the interface variable, but so far no luck. The pre-processor is choking with a IllegalArgumentException Couldn't make a guess for CallbackRun...
The interface looks generally like:
public interface callbackRun {
void runOnFinish();
}
With the broad strokes shape of my class defined as:
#JSONMapper
#JsonIgnoreProperties(ignoreUnknown=true)
public class itemInventory {
public static itemInventory_MapperImpl MAPPER = new itemInventory_MapperImpl();
private static final List<item> itemList = new ArrayList<>();
private callbackRun responseHandler = null;
/ * other variables, getters setters here */
}
What is the best method of getting GWT-jackson-APT to ignore this interface? Or do I have to completely redefine all my objects to remove my callback function references?
You can use #JsonIgnore by annotating the field
#JSONMapper
public class itemInventory {
public static itemInventory_MapperImpl MAPPER = new itemInventory_MapperImpl();
private static final List<item> itemList = new ArrayList<>();
#JsonIgnore
private callbackRun responseHandler = null;
/ * other variables, getters setters here */
}
The field will not be serialized when writing object to JSON and it will be ignored when reading object from JSON. You can always check the generated mappers and you will see a method initIgnoredProperties in the generated deserializer, also the ignored field will not be included in the generated serializer.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I have two classes which have identical behaviour, except class SnakeCaseyMapper uses snake_case constant fields and class CamelCaseyMapper uses camelCase constant fields.
Before requiring two of these classes, my logic looked roughly like:
public class Mapper {
public static final String FIELD = "snake_casey_field";
// Lots of other constant fields ...
public Foo map(Bar bar) {
// Some logic that makes use of the constant FIELDs
}
}
public class ClassThatsDoingLogic {
var mapper = new Mapper();
var result = mapper.map(bar);
}
Now I require this same method, map(Bar bar) but with camelCase constants, as well as the original implementation with snake_case.
My idea was to make use of abstract classes:
public abstract class Mapper {
public String field; // Not instantiated here
// Lots of other member variable fields ...
public Foo map(Bar bar) {
// Some logic that makes use of the constant FIELDs
}
}
public class SnakeCaseyMapper extends Mapper {
public SnakeCaseyMapper() {
field = "snake_casey_field";
// Lots of other fields instantiated
}
}
public class CamelCaseyMapper extends Mapper {
public CamelCaseyMapper() {
field = "camelCaseyField";
// Lots of other fields instantiated
}
}
public class ClassThatsDoingLogic {
var snakeCaseyMapper = new SnakeCaseyMapper();
var result = snakeCaseyMapper.map(snakeCaseyBar);
var camelCaseyMapper = new CamelCaseyMapper();
var result = camelCaseyMapper.map(camelCaseyBar);
}
This way both classes use the same method logic in map() without duplicating the code. However, I think I lose the finality of the constant fields I had originally. Is there a way around this? Is there a way of handling this problem I'm missing?
As #Kayaman suggested, inheritance should be avoided, and in your case, it is all about parameterisation. If you can do it via configuration loading it would be great.
A solution in the middle, could be possibly to instantiate a private constructor with all the arguments needed, and then provide one public constructor that would call the private one, setting the arguments needed under condition. (Note: untested code in examples below)
public class Mapper {
enum MapperType {
CamelCase,
SnakeCase
}
// Never define a public property. Use setters
// and getters to modify them outside the class,
// preserving the encapsulation principle.
private MapperType mType;
private int mProperty1;
public Mapper(MapperType type) {
this(type, type == MapperType.CamelCase ? 100 : 200);
}
private Mapper(MapperType type, int mProperty1) {
this.mType = type;
this.mProperty1 = property1;
// More properties here
}
}
A deviation to this, would also be to use Factory-ish pattern (Note: take the definition with a grain of salt, as normally, a factory can be used in order to generate instances of different derived classes sharing the same base class).
public class Mapper {
enum MapperType {
CamelCase,
SnakeCase
}
private MapperType mType;
private int mProperty1;
public Mapper(MapperType type, int mProperty1) {
this.mType = type;
this.mProperty1 = property1;
// More properties here
}
}
Then, you can create a Factory "Wrapper" class for the initialization:
public static class MapperFactory {
public static Mapper instantiate(Mapper.MapperType type) {
// Dummy example. Notice that we change all parameters.
// a dispatch table can also be considered to avoid switching.
switch(type) {
case Mapper.MapperType.CamelCase:
return new Mapper(Mapper.MapperType.CamelCase, 100);
case Mapper.MapperType.SnakeCase:
return new Mapper(Mapper.MapperType.SnakeCase, 200);
}
}
}
and then, you can do:
Mapper m = MapperFactory.instantiate(Mapper.MapperType.CamelCase);
Consider though that, if you are just adding such a few parameters, such implementation is overengineering, just to show you an example. Use it only if you have LOTS of parameters for your objects and you want ti. In simple scenarios, just call the Mapper class with the appropriate parameters, or make a simple conditional check upon initialization.
Also, regarding the difference between snake_case and camelCase fields, you can use regex in order to distinguish and properly initialize upon condition, but my sense is that you are asking mainly for the proper code segmentation, rather than fields distinction based on the style they are written.
To add to my comments. Since inheritance can be used when there's different behaviour, this is definitely not the right place for it.
Below are 3 examples with "least effort", although they still require at least the amount of lines that you have fields in the mapper.
public class Mapper {
private final String FIELD;
private String FIELD2 = "defaultCamelCase";
private final String FIELD3;
public Mapper(boolean snakeCase) {
// This would work for final instance fields
FIELD = snakeCase ? "snakey_case_field" : "camelCaseField";
// or fields having default values
if(snakeCase) {
FIELD2 = toSnakeCase(FIELD2);
// or some kind of similar mechanism
}
// or final instance fields with a private constructor helper
// that returns either the parameter as-is, or converts it
FIELD3 = initField("fieldName", snakeCase);
}
private String initField(String field, boolean snakeCase) {
if(!snakeCase)
return field;
return Arrays.stream(field.split("(?=[A-Z])")).map(String::toLowerCase).collect(Collectors.joining("_"));
}
}
I'm serializing some existing objects with Jackson 2.22, leveragin the MixIn feature to decouple the real object from the Jackson annotations configuration.
Actually my mixin is an interface that declares the same methods of the target class and annotates them, here's an example.
Target class:
public class Product {
// ...
public String getName();
public String getDescription();
public String getPrice();
public String getFinalPrice();
public String getDiscount();
// ...
}
and the mixin:
public interface ProductApi {
#JsonProperty
public String getName();
#JsonProperty("price")
public String getFinalPrice();
}
My JSON should have some more informations, computed from several methods or fields of the target class.
Is this even possible in Jackson?
I tried turning the mixin in a class and adding a new method there, but that didn't work.
public class ProductApi {
#JsonProperty
public String getName();
#JsonProperty("price")
public String getFinalPrice();
#JsonProperty("images")
public List<String> getImages() { /* ... */ }
}
I guess this is because the mixin only provides annotations for the target class, but is the latter that is read for serialization.
Of course, if I change the object to be serialized with a new subclass that contains the new method I need, that works, but the objects come from our services layers, and this would mean I have to rewrite all those methods.
I'm using Jackson with Jersey, so don't want to change Jackson with another library.
Here's how I did it.
The solution is to specify a custom JsonSerializer implementation to the field getter.
First of all, I changed the mixin interface to a class that extends the entity (target) class, so that it can access the target class data.
public class ProductApi extends Product {
#JsonProperty
#Override
public String getName() {
return super.getName();
};
// ...
}
Next, I implemented the JsonSerializer that would create the derived property I want:
public static class ImagesSerializer extends JsonSerializer<String> {
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Product p = (Product) jgen.getCurrentValue();
int num = p.getNumberOfImages();
List<String> imgs = new ArrayList<String>(num);
for(int i = 0; i < num; i++) {
String src = "/include/images/showImage.jsp?"+"id="+p.getId()+"&number="+i;
imgs.add(src);
}
provider.defaultSerializeValue(imgs, jgen);
}
}
This is a really simple implementation, more safety checks should be done.
What this does is, basically, retrieve the whole entity instance from the JSON generator, build up a custom object and then ask Jackson to serialize it.
I implemented it inside my ProductApi as a static class, but just for simplicity.
Finally, the serializer needs to be bound to the JsonProperty annotated field:
public class ProductApi extends Product {
#JsonProperty
#Override
public String getName() {
return super.getName();
};
// ...
#JsonSerialize(using=ImagesSerializer.class)
#JsonProperty("images")
#Override
public String getImage() { // in my entity this returns an image number, whereas in my JSON I want a list of URLs
return "";
}
// ...
}
As a side note, it seems that the returned value of the getImage() method is not used.
Why don't you just make some fields, which should be serialized and use Gson for it?
I want to serialize a POJO class which is not under my control, but want to avoid serializing any of the properties which are coming from the superclass, and not from the final class. Example:
public class MyGeneratedRecord extends org.jooq.impl.UpdatableRecordImpl<...>,
example.generated.tables.interfaces.IMyGenerated {
public void setField1(...);
public Integer getField1();
public void setField2(...);
public Integer getField2();
...
}
You can guess from the example that that this class is generated by JOOQ, and inherits from a complex base class UpdatableRecordImpl which also has some bean property-like methods, which cause problems during the serialization. Also, I have several similar classes, so it would be good to avoid duplicating the same solution for all of my generated POJOs.
I have found the following possible solutions so far:
ignore the specific fields coming from superclass using mixin technique like this: How can I tell jackson to ignore a property for which I don't have control over the source code?
The problem with this is that if the base class changes (e.g., a new getAnything() method appears in it), it can break my implementation.
implement a custom serializer and handle the issue there. This seems a bit overkill to me.
as incidentally I have an interface which describes exactly the properties I want to serialize, maybe I can mixin a #JsonSerialize(as=IMyGenerated.class) annotation...? Can I use this for my purpose?
But, from pure design point of view, the best would be to be able to tell jackson that I want to serialize only the final class' properties, and ignore all the inherited ones. Is there a way to do that?
Thanks in advance.
You can register a custom Jackson annotation intropector which would ignore all the properties that come from the certain super type. Here is an example:
public class JacksonIgnoreInherited {
public static class Base {
public final String field1;
public Base(final String field1) {
this.field1 = field1;
}
}
public static class Bean extends Base {
public final String field2;
public Bean(final String field1, final String field2) {
super(field1);
this.field2 = field2;
}
}
private static class IgnoreInheritedIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return m.getDeclaringClass() == Base.class || super.hasIgnoreMarker(m);
}
}
public static void main(String[] args) throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new IgnoreInheritedIntrospector());
final Bean bean = new Bean("a", "b");
System.out.println(mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(bean));
}
}
Output:
{
"field2" : "b"
}
You can override the superclass' methods which you'd like to prevent from being output and annotate them with #JsonIgnore. The override shifts the control of property creation to the subclass while enabling its ability to filter it from the output.
For instance:
public class SomeClass {
public void setField1(...);
public Integer getField1();
public void setField2(...);
public Integer getField2();
#Override
#JsonIgnore
public String superClassField1(...){
return super.superClassField1();
};
#Override
#JsonIgnore
public String superClassField2(...){
return super.superClassField2();
};
...
}
You can use this as well instead of unnecessary overrides
#JsonIgnoreProperties({ "aFieldFromSuperClass"})
public class Child extends Base {
private String id;
private String name;
private String category;
}
The good use of inheritance is that the child classes extend or add functionality. So the usual way is to serialize the data.
A workarround would be to use a Value Object (VO) or Data Transfer Object (DTO) with the fields you need to serialize. Steps:
Create a VO class with the fields that should be serialized.
Use BeanUtils.copyProperties(target VO, source data) to copy the properties
Serialize the VO instance.
Add the following annotation in your Base Class :
#JsonInclude(Include.NON_NULL)
I want to understand why it is possible to create and fill an object that only got private variables and an overwritten constructor.
Code example:
public class Test {
public static void main(String[] args) {
String json = "{\"id\":\"123546\"}";
Gson gson = new Gson();
Participant p = gson.fromJson(json, Participant.class);
System.out.println(p.getId());
}
}
public class Participant {
private int id;
public Participant() {
}
public int getId() {
return id;
}
}
It prints "123546" correctly.
The gson.fromJson Method has following signature: <T> T: fromJson(String json, Class<T> classOfT)
http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/index.html
Gson, like many other JSON parser/generator libraries, uses reflection to populate fields, either directly or through methods.
Through reflection you can access public and non-public members of a class and modify them (fields) or invoke them (methods and constructors).
Your starting point should be the Class class, which provides methods to retrieve the Fields, Methods, and Constructor's of a class.
Gson uses the Class object you provide, Participant.class, to find out all the fields it needs to populate. It parses the JSON and (attempts to) maps them, one by one.
I have a class that, when serialized, should serialize one of its members in its place. My class is:
#JsonSerialize(using = MyClassSerializer.class)
public class MyClass implements Serializable {
/**
* A default ID for this class for serialization.
*/
private static final long serialVersionUID = 1L;
/**
* A member of this object.
*/
private final OtherClass otherClass;
...
/**
* Returns the instance of the member object.
*
* #return The instance of the member object.
*/
public OtherClass getOtherClass() {
return otherClass;
}
}
To accomplish this, I created a very simple custom serializer:
public class MyClassSerializer extends JsonSerializer<MyClass> {
/**
* Serializes only the OtherClass field.
*/
#Override
public void serialize(
final MyClass myClass,
final JsonGenerator generator,
final SerializerProvider provider)
throws IOException, JsonProcessingException {
// Write the schema.
generator.writeObject(myClass.getOtherClass());
}
}
This is the easiest (and, I believe, most correct) way to do something like this. Even if I wanted to, writing a custom serializer for OtherClass would be extremely complex because it is an abstract root class. This can be accomplished through Jackson, however, with a few annotations:
#JsonTypeInfo(
use = Id.NAME,
include = As.PROPERTY,
property = OtherClass.JSON_KEY_TYPE,
defaultImpl = OtherClassDefault.class)
#JsonSubTypes({
#JsonSubTypes.Type(
value = SubOtherClass1.class,
name = SubOtherClass1.TYPE_ID),
#JsonSubTypes.Type(
value = SubOtherClass2.class,
name = SubOtherClass2.TYPE_ID),
#JsonSubTypes.Type(
value = SubOtherClass3.class,
name = SubOtherClass3.TYPE_ID),
#JsonSubTypes.Type(
value = SubOtherClass4.class,
name = SubOtherClass4.TYPE_ID),
#JsonSubTypes.Type(
value = SubOtherClass5.class,
name = SubOtherClass5.TYPE_ID) })
#JsonAutoDetect(
fieldVisibility = Visibility.DEFAULT,
getterVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
creatorVisibility = Visibility.DEFAULT)
public abstract class OtherClass implements Serializable {
...
}
I have tested this using Jackson to serialize and deserialize instances of MyClass, and it works exactly as intended.
My application code is a little more complicated. I have a ContainerClass that has a member of type MyClass. I attempt to serialize an instance of ContainerClass through MongoJack:
// Get the authentication token collection.
JacksonDBCollection<ContainerClass, Object> collection =
JacksonDBCollection
.wrap(
MongoBinController
.getInstance()
.getDb()
.getCollection(COLLECTION_NAME),
ContainerClass.class);
// Save it.
collection.insert(container);
However, I receive the following error:
...
Caused by: java.lang.IllegalArgumentException: can't serialize class my.package.MyClass
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:270)
...
I can get it to work if I remove the #JsonSerialize from MyClass, however this results in the entire MyClass instance being serialized, which is not what I want. This makes me almost sure that the problem lies in my custom serializer, but I am not sure how else I am supposed to write it.
Thank you in advance.
One quick note that may help: when using polymorphic types, method called will be:
serializeWithType(...)
and not serialize(...). So you will need to implement that method; usually it will be something as simple as:
typeSer.writeTypePrefixForObject(value, jgen);
// implement actual content serialization, or delegate here:
this.serialize(...);
typeSer.writeTypeSuffixForObject(value, jgen);
but you may want to have a look at standard Jackson serializers. The only real distinction is that whereas serialize needs to output START_OBJECT, END_OBJECT directly, here we have to ask TypeSerializer to add those. This is necessary because type id inclusion may actually change these (I can elaborate on this, but for now that should be enough).
However: there may be much easier solution here. If you can add #JsonValue annotation on member you want to use instead of whole object (either directly, or via mix-in), that should do the trick:
#JsonValue
public OtherClass getOtherClass()...
and if you need to deserialize, you can use #JsonCreator like:
#JsonCreator
public MyClass(OtherClass surrogate) { ... }