I'm trying to implement wrapper class, that will be serialized by Jackson with omitting fields depending on runtime configuration:
class Partial<T> {
T value;
List<String> fieldsToSkip;
}
This
class Example{int a=1; int b=2; int c=3;}
new Partial(new Example(), asList("b"));
suppose to be serialized to {"a":1, "c":3}.
#JsonUnwrapped with #JsonFilter seems to be right approach here. The problem is that the filter works on value field level, where there's no access to host Partial instance.
What is the best way to implement such thing?
You can create at run time an ObjectWriter with a filter also defined at run time and use it to write the value in Partial:
SimpleBeanPropertyFilter filter =
SimpleBeanPropertyFilter.serializeAllExcept(new HashSet<>(partial.fieldsToSkip));
FilterProvider fp = new SimpleFilterProvider().addFilter("exampleFilter", filter);
String text = objectMapper.writer(fp).writeValueAsString(partial.value);
and tell Jackson that this filter should be applied to Example class:
#JsonFilter("exampleFilter")
class Example{int a=1; int b=2; int c=3;}
You may want to change fieldsToSkip to a Set<String>
Related
So I am writing a Telematics application and we are slowly building up mappers for DTOs. There will soon be over 100 but right now we have 3. We want to send all messages to our mapper but when we do and Orika doesn't know about it, it throws an exception.
I need a .isMapperAvailable(class, class) method but cannot find one. I did find a .existsRegisteredMapper(Type, Type) but can't figure out how it works. Any help?
I think you could use it like this:
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
ClassMapBuilder<Foo, Bar> map = mapperFactory.classMap(Foo.class, Bar.class);
Type<Foo> fooType = map.getAType();
Type<Bar> barType = map.getBType()
boolean exists = mapperFactory.existsRegisteredMapper(fooType, barType, false);
There is one flaw with existsRegisteredMapper() method.
Suppose you want to different type of mapping for A to B and B to A.
In this case, this method returns true for both method calls:
mapperFactory.existsRegisteredMapper(A, B, true);
mapperFactory.existsRegisteredMapper(B, A, true);
the usual way to serialize to json and back is:
String catStr = mapper.writeValueAsString(cat);
Cat catOut = mapper.readValue(catStr, Cat.class);
Is there a way to add (maybe with annotation ) the type to the json on serialization and let the mapper take the value from it when it deserialize it?
so I can do the following
Object obj = mapper.readValue(catStr);
and later...
Cat catOut = (Cat)obj;
Thanks.
Sort of. You can add a property to the serialization which will indicate what class it is. And then when deserializing it, Jackson deduces the class automatically.
But you cannot deserialize it as Object, you need some base class/interface for all objects you want to behave like this. And that interface needs to use the #JsonTypeInfo annotation, signalling Jackson when deserializing this base class use the property class to distinguish which type.
Example:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
private abstract class Base {
}
private class A extends Base {
private final int i;
#JsonCreator
public A(#JsonProperty("i") int i) {
this.i = i;
}
}
When serialized will be:
{"#class":"com.test.A","i":3}
Testing code:
A a = new A(3);
String str = mapper.writeValueAsString(a);
Base base = mapper.readValue(str, Base.class);
I am trying to serialize a JSON object using Jackson and save into a mysql database using hibernate. All fields of my POJO class are able to be serialized except for any field that isn't a primitive.
public class Teacher {
private Set<Student> students;
private int id;
// getters and setters
}
In this case it would fail on students, creating an infinite recursive loop through the reference chain. I can stop it with #JsonIgnoreProperty but I want this field to be serialized. I am serializing my object like so:
ObjectMapper mapper = new ObjectMapper();
Teacher myTeacher = new Teacher();
mapper.writeValueAsString(teacher);
The only workaround I can think of is appending a string to the end of teacher while still ignoring the property but I am not sure if I will be able to read students as a JsonNode from the tree if I do this.
A way around this would be to use a pure Array or an ArrayList which are serialized fine with Jackson.
For example, I can serialize a class with all these parameters :
public class Map{
private short [][] someMapType;
private short [][] someOtherMap;
private ArrayList<Mill> someMills, otherMills;
private ArrayList<OtherPOJO> myPOJOList;
private String action = "myDefaultAction";
...
}
Where Mill and OtherPOJO are class with not much more than a couple of arrays and other primitives : pure POJOs.
It works fine both ways with Jackson and MongoJack (jackson serializer for MongoDb).
If you can't get away from the set than you have to understand properly what is the fundamental data structure in a set. This should help but you are probably already aware of that.
A way to work around this structure limitation would be to create non-dumb getters and setters. The main disadvantage behind this method is that you run one more for loop over all your elements every time you serialize or de-serialize. This might slightly reduce performance.
The getter is fairly simple :
public Student[] getStudents(){
return this.students.toArray();
}
And the setter is also pretty trivial :
public void setStudents(Student[] students){
this.students = new Set<Student>(); // Or anything that builds the right Set for you
for(int i = 0; i < students.length; i++){
this.students.add(students[i]);
}
}
I wrote it quickly, let me know if there is any bug.
I hope it helps!
Found a decent workaround:
#JsonIgnore
public Set<Student> getStudents() {
return students;
}
#JsonProperty("Students")
public String getStudentsForJson() {
String[] studentNames = new String[this.students.size()];
int i = 0;
for(Student student : this.students) {
studentNames[i] = student.getName();
i++;
}
return StringUtils.join(studentNames, ", ");
}
This saves all student names as one string which I'm able to easily serialize and deserialize as a field.
I have an existing internal data model for a Picture, as follows:
package test.model;
public class Picture {
private int height, width;
private Format format;
public enum Format {
JPEG, BMP, GIF
}
// Constructor, getters and setters, hashCode, equals, toString etc.
}
I now want to serialize it using protocol buffers. I've written a Picture.proto file that mirrors the fields of the Picture class and compiled the code under the test.model.protobuf package with a classname of PictureProtoBuf:
package test.model.protobuf;
option java_package = "test.model.protobuf";
option java_outer_classname = "PictureProtoBuf";
message Picture {
enum Format {
JPEG = 1;
BMP = 2;
GIF = 3;
}
required uint32 width = 1;
required uint32 height = 2;
required Format format = 3;
}
Now I am now assuming that if I have a Picture that I want to serialize and send somewhere I have to create a PictureProtoBuf object and map all the fields across, like so:
Picture p = new Picture(100, 200, Picture.JPEG);
PictureProtoBuf.Picture.Builder output = PictureProtoBuf.Picture.newBuilder();
output.setHeight(p.getHeight());
output.setWidth(p.getWidth());
I'm coming unstuck when I have an enumeration in my data model. The ugly way that I'm using right now is:
output.setFormat(PictureProtoBuf.Picture.Format.valueOf(p.getFormat().name());
However, this is prone to breakage and relies on the enumeration name being consistent between my internal data model and the protocol buffer data model (which isn't a great assumption as enumeration names within .proto files need to be unique). I can see me having to hand-craft switch statements on enumerations if the .name() call from the internal model doesn't match the protobuf-generated enumeration name.
I guess my question is whether I'm going about this the right way? Am I supposed to scrap my internal data model (test.model.Picture) in favour of the protobuf-generated one (test.model.protobuf.PictureProtoBuf)? If so, how can I implement some of the niceties that I have done in my internal data model (e.g. hashCode(), equals(Object), toString(), etc.)?
Although the existing answers are good, I decided to go a bit further with Marc Gravell's suggestion to look into protostuff.
You can use the protostuff runtime module along with the dynamic ObjectSchema to create schemas at runtime for your internal data model
My code now reduces to:
// Do this once
private static Schema<Picture> schema = RuntimeSchema.getSchema(Picture.class);
private static final LinkedBuffer buffer = LinkedBuffer.allocate(DEFAULT_BUFFER_SIZE);
// For each Picture you want to serialize...
Picture p = new Picture(100, 200, Picture.JPEG);
byte[] result = ProtobufIOUtil.toByteArray(p, schema, buffer);
buffer.clear();
return result;
This is a great improvement over the Google protobuf library (see my question) when you have lots and lots of attributes in your internal data model. There is also no speed penalty that I can detect (with my use cases, anyway!)
If you have control over your internal data model, you could modify test.model.Picture so that the enum values know their corresponding protobuf equivalent, probably passing in the correspondence to your enum constructors.
For example, using Guava's BiMap (bidirectional map with unique values), we get something like
enum ProtoEnum { // we don't control this
ENUM1, ENUM2, ENUM3;
}
enum MyEnum {
ONE(ProtoEnum.ENUM1), TWO(ProtoEnum.ENUM2), THREE(ProtoEnum.ENUM3);
static final ImmutableBiMap<MyEnum, ProtoEnum> CORRESPONDENCE;
static {
ImmutableBiMap.Builder<ProtoEnum, MyEnum> builder = ImmutableBiMap.builder();
for (MyEnum x : MyEnum.values()) {
builder.put(x.corresponding, x);
}
CORRESPONDENCE = builder.build();
}
private final ProtoEnum corresponding;
private MyEnum(ProtoEnum corresponding) {
this.corresponding = corresponding;
}
}
and then if we want to look up the MyEnum corresponding to a ProtoEnum, we just do MyEnum.CORRESPONDENCE.get(protoEnum), and to go the other way, we just do MyEnum.CORRESPONDENCE.inverse().get(myEnum) or myEnum.getCorresponding().
One way is to only keep the generated enum:
package test.model;
public class Picture {
private int height, width;
private PictureProtoBuf.Picture.Format format;
// Constructor, getters and setters, hashCode, equals, toString etc.
}
I've used this a few times, it may or may not make sense in your case. Using the protobuf generated classes as you data model (or extending them to add functionality), is never recommended, though.
Suppose I have a json object that looks like:
{
id: 1,
name: "john doe"
spouse: 2
}
and the class I want it to deserialize it to:
class Person{
private Long id;
private String name;
private Person spouse;
//getters/setters
}
Is there any way to tell jackson to expand the spouse: 2 property into a new Person POJO with id=2 when deserializing the JSON?
I have run into this issue as a result of deserializing JSON into persistent entities and would like to be able to easily persist the relationships between these entities.
Aside from a full deserializer, there is a simpler way: define a POJO with a single int-arg constructor like so:
class Person {
int id;
public Person(int id) {
this.id = id;
}
}
This actually works, as Jackson will try to find limited number of special constructors (single-arg public constructors that that take String, int, long, double or boolean).
You can optionally also denote these with #JsonCreator -- and if constructor is not public, you must do it, to make it discoverable. But for public ctors this is not needed.
It is impossible of course for Jackson to infer a fully populated Person object representing the spouse from the number 2. You would likely need to register a custom deserializer that checks if the input is an integer, and if so, looks up the spouse from wherever it is stored. I have not done this kind of thing for classes that contain references to themselves (e.g. your Person contains a Person) so I can only give you rough guidance.
I believe this may only work with Jackson version 1.9 or later. Basically, you can register a module with the object mapper that tells Jackson to use a custom deserializer.
SimpleModule module = new SimpleModule("PeopleModule", new Version(1, 1, 0, null);
module.addDeserializer(Person.class, new JacksonPersonDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Alternately on the Person class itself, you can do something like:
class Person {
#JsonDeserialize(using=JacksonPersonDeserializer.class)
Person spouse;
}
This works before 1.9 but pollutes your object. Either way, you will need to write a custom deserializer.