As mentioned here, I know that I can convert Java objects to JSON (with Jackson)
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(objectToBeConverted);
I know that I can exclude fields from being included in the JSON string using the #JsonIgnore annotation, but what if I want to convert the same class to JSON multiple times, but each time choosing different fields to ignore?
For example, if I have a class
class Foo {
int a;
int b;
...
}
can I do something like
Foo foo = new Foo();
String json1 = ow.writeValueAsString(foo).excludeField('b');
String json2 = ow.writeValueAsString(foo).excludeField('a');
so that the resulting strings look like
// json1
{
a: 1234
}
// json2
{
b: 5678
}
If Jackson can't do it, maybe Gson can? Or another library?
You can try using different mix-in interfaces. I found two ways to do this.
Use methods for reading properties. You can then create a mix-in class that only defines the properties to exclude:
public interface ExludeA {
#JsonIgnore
int getA();
}
Use #JsonIncludeProperties to not tell which properties to exclude, but which properties to include:
#JsonIncludeProperties({ "b", "c" })
public interface ExludeA {
}
In both cases, add that mix-in to the object mapper:
objectMapper.addMixIn(Foo.class, ExcludeA.class);
There is one very, very important thing though - you must use a new ObjectMapper for each mix-in. If you use an ObjectMapper instance to serialize a Foo instance without mix-ins, then adding the mix-in won't help. That's probably because ObjectMapper instances cache some stuff.
Here is a simple approach if you could box up all primitive type in Foo.
For example: int -> Integer, boolean -> Boolean
#JsonInclude(Include.NON_NULL)
class Foo {
Integer a;
Integer b;
...
}
Then, just make a copy of Foo and set the property which you want to ignore to null.
Foo foo = new Foo();
foo.setA(1234);
foo.setB(5678);
Foo foo1 = objectMapper.readValue(objectMapper.writeValueAsString(foo), Foo.class); // make a copy of Foo
foo1.setB(null); // force to ignore B
String json1 = ow.writeValueAsString(foo1); // it will be {a:1234}
There is multiple solution based on your need:
First:
You can just define two different DTO for your purpose and every time you need to each one just use it.
Second:
You can use #JsonInclude(JsonInclude.Include.NON_NULL) annotation for the properties:
class Foo {
#JsonInclude(JsonInclude.Include.NON_NULL)
int a;
#JsonInclude(JsonInclude.Include.NON_NULL)
int b;
}
P.S: You can use this annotation on class level as:
#JsonInclude(JsonInclude.Include.NON_NULL)
class Foo {
int a;
int b;
}
Third:
Use can define a filter to ignore properties based on different conditions.
Define a simple class for your filter:
public class YourConditionalFilter {
#Override
public boolean equals(int a) {
return a == 1234;
}
}
And then add this filter as annotation on top of the property:
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = YourConditionalFilter.class)
int a;
Related
Since I'm a newbie, I would like to know if there is a better way to code this.
Let say we have batch (spring) where we have downloader/processor/mapper/writer for every type of file we receive since we have customized logic for each file type. X number of Mapper , X number of processor for X number of file types.
Currently looking into templatize the code so not much changes may be required when new type is introduced. Below is my idea. so let say mapper, we have different objects for different file types and all of them will be converted to object of Class CustomObject as below. mapper bean in sample spring context
bean id = "file1Mapper" class = "com.filemapper.file1Mapper"
and it invokes file1Mapper class which has mapping logic. Same for other files.
This is what I'm coming up with to avoid all those file1mapper, file2mapper...... instead one generic mapper which does all together, but looking for better solutions,
public class GMapper{
public <T> CustomObject map(T item){
CustomObject customObject = new CustomObject()
.WithABCDetails(getABCDetails(item));
}
private <T> XYZDetails getABCDetails(T item) {
ABCDetails details = new ABCDetails();
if( item instanceof A){
A a = (A)item;
// read a and map it to ABCDetails object
}
if( item instanceof B){
B b = (B)item;
// read b and map it to ABCDetails object
}
...
...
// repeat this if loop for mapping all file types.
return details;
}
}
Sample jsons
class ABCDetails{
// JsonProperty
Object1 ob1;
Object2 ob2;
Integer d;
}
class Object1{
// JsonProperty
Object3 ob3;
String abc;
String def;
}
class Object2{
// JsonProperty
String ab;
Integer e;
}
class A{
// JsonProperty
String e;
String d; // ex, this is mapped to Object 2 String "ab"
}
This does't look so professional and I believe there might be better ways to do it. Can someone please share an example or explanation on how can this code be made better. I also reading Functional interface to see if that could help.
Thanks in advance.
It is impossible to understand what you need. So I will give some common advice.
Format your code - use tabs/spaces to indent.
Do not put capital letters together - replace ABCDetails with AbcDetails. No one cares how real world name looks like.
Do not write meaningless comments - say no to // JsonProperty
Name variables so that someone can understand what they are supposed to store - avoid {Object1 ob1; Object2 ob2; Integer d;}
Do not write if ... else if ... else if ... or case when ... since this scales badly. Use Map. Examples below.
And a general solution to your problem: use plugin architecture - the best thing (and maybe the only thing) that OOP can offer. Just make all your processors implement common interface. And to work with plugins use dispatcher pattern.
First create all processors.
public interface FileProcessor {
String extension();
void process(String filename);
}
#Component
public final class CsvFileProcessor implements FileProcessor {
public String extension() {
return "csv";
}
public void process(String filename) {
/* do what you need with csv */
}
}
#Component
public final class JsonFileProcessor implements FileProcessor {
public String extension() {
return "json";
}
public void process(String filename) {
/* do what you need with json */
}
}
Then inject them into your dispatcher. Do not forget to process errors, for example, some files may not have suffix, for some files you will not have processor, etc.
#Component
public final class FileDispatcher {
private final Map<String, FileProcessor> processorByExtension;
#Autowired
public FileDispatcher(List<FileProcessor> processors) {
processorByExtension = processors.stream().collect(Collectors.toMap(p -> p.extension(), p -> p));
}
public void dispatch(String filename) {
String extension = filename.split("//.")[1];
processorByExtension.get(extension).process(filename);
}
}
Now if you need to support new file format you have to add only one class - implementation of FileProcessor. You do not have to change any of already created classes.
I have a POJO that contains the following attributes
public class Example {
#JsonProperty("inputFoo")
private String foo
#JsonProperty("inputBar")
private String bar
#JsonProperty("inputBaz")
#JsonDeserialize(using = MyDeserializer.class)
private Set<String> baz
}
The JSON that I am working with to represent this data currently represents the baz attribute as a single string:
{"inputFoo":"a", "inputBar":"b", "inputBaz":"c"}
I am using the Jackson ObjectMapper to attempt to convert the JSON to my POJO. I know that the input baz String from the JSON wont map cleanly to the Set that I am trying to represent it as, so I defined a custom Deserializer:
public class MyDeserializer extends StdDeserializer<Set<String>> {
public MyDeserializer(){}
public MyDeserializer(Class<?> vc) {
super(vc);
}
public Set<String> deserialize(JsonParser p, DeserializationContext cxt) throws IOException, JsonProcessingException {
String input = p.readValueAs(String.class);
Set<String> output = new HashSet<>();
if(input != null) {
output.add(input);
}
return output;
}
}
I am getting an IllegalArgumentException referencing the "inputBaz" attribute, which I can provide details on. Does anyone see any obvious issue with my deserializer implementation? Thanks
You do not need to implement custom deserialiser, use ACCEPT_SINGLE_VALUE_AS_ARRAY feature. It works for sets as well:
Feature that determines whether it is acceptable to coerce non-array
(in JSON) values to work with Java collection (arrays,
java.util.Collection) types. If enabled, collection deserializers will
try to handle non-array values as if they had "implicit" surrounding
JSON array. This feature is meant to be used for
compatibility/interoperability reasons, to work with packages (such as
XML-to-JSON converters) that leave out JSON array in cases where there
is just a single element in array. Feature is disabled by default.
See also:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token
Replace the 2 constructors with this no-arg constructor:
public MyDeserializer() {
super(TypeFactory.defaultInstance().constructCollectionType(Set.class, String.class));
}
ACCEPT_SINGLE_VALUE_AS_ARRAY as suggested is a good option.
Maybe your actual problem is more complicated but if not you could also try #JsonCreator instead of custom deserializer. Like:
public class Example {
#JsonCreator
public Example(#JsonProperty("inputFoo") String foo,
#JsonProperty("inputBar") String bar,
#JsonProperty("inputBaz") String strBaz) {
this.foo = foo;
this.bar = bar;
this.baz = new HashSet<>();
baz.add(strBaz);
}
private String foo;
private String bar;
private Set<String> baz;
}
Just to show that in more general case you might avoid implementing custom deserializer with #JsonCreator also but still make some simple conversions.
Lets say I have a group of classes A,B,C:
public class A:
int number;
public class B:
int number;
String address;
public class C:
int orderNumber;
How can i deserialize a Json string which contains only these classes, but in an unknown order (using Gson, in Java)? For example:
{//A
"number" : 3
}
//C
{
"orderNumber": 10
}
//B
{
"number" : 5
"address" : "New York"
}
//C
{
"orderNumber": 1
}
Thank you very much!
Answer by pirho is clean and easy if, like he said, your classes are simple as you've provided. But if that isn't the case, you can write your own deserializer.
public class PayloadJsonDeserializer implements JsonDeserializer {
#Override
public Object deserialize(JsonElement elm, Type type, JsonDeserializationContext context) throws JsonParseException {
// create java objects based on the properties in the json object
JsonPrimitive orderNumber = elm.getAsJsonObject().getAsJsonPrimitive("orderNumber");
if(!orderNumber.isJsonNull()) {
return new C(orderNumber.getAsInt());
}
return null;
}
}
Register your custom deserializer with Gson.
GsonBuilder gsonBuilder = new GsonBuilder()
.registerTypeAdapter(PayloadJson.class, new PayloadJsonDeserializer());
Gson gson = gsonBuilder.create();
Use it to deserialize your json.
gson.fromJson(jsonString, PayloadJson[].class);
This is not a generic or anyway a great strategy to do this in general if you have more complex classes with more fields.
But if the classes you want to deserialize are as simple as you provide as an example then create a class having all these fields
#Getter
public class Z {
private Integer orderNumber;
private Integer number;
private String address;
}
You will get a list of Zs and depending on which of the field are null or not null you can -if needed - later construct A, B or C from Z.
If classes to deserialize are more complex you anyway need to create some kind of a mechanism that determines what is the class to parse and to return. It could be like user1516873 suggested in the comment
Collection<Map<String,String>>
so for each item you would need to determine by what fields are present in that map to what class - A,B or C - item would be constructed.
I have a wrapper json object for example
{
"id": 23,
"name": "teset",
"type": "person",
"_data": {
"address": 23432
}
}
my java object would look like this
public class Wrapper<D>{
private Integer id;
private String type;
#JsonProperty("_data")
private D data;
...
}
i cannot find a way to have the object mapper do this
Wrapper<Person> wrapped = objectMapper.readValue(jsonStream,Wrapper.class);
is this not supported, i haven't been able to find much information about generics in Jackson.
There are a few problems with your code:
The main issue is that you are not specifying the desired parametrized type of Wrapper in your readValue invocation. You can fix this by using (simplified form): Wrapper<Person> wrapped = om.readValue(json, new TypeReference<Wrapper<Person>>() {});
Also, your JSON features a name property that is not apparently present in your Wrapper class. You either have it and haven't posted it, or you can configure your ObjectMapper to ignore unknown properties: objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Here's an example:
public static class Wrapper<D> {
// making fields public for simplicity,
// use public getters and private fields of course
public Integer id;
public String type;
#JsonProperty("_data")
public D data;
}
public static class Person {
// adding address field as a public int,
// same as above, encapsulate properly in real life
public int address;
}
Then, in a main method somewhere...
ObjectMapper om = new ObjectMapper();
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// your example JSON
String json = "{\"id\":23,\"name\":\"test\",\"type\":\"person\",\"_data\":"
+ "{\"address\":23432}}";
Wrapper<Person> wrapped = om.readValue(
json, new TypeReference<Wrapper<Person>>() {}
);
// printing class/hashCode of the resolved generic type
System.out.println(wrapped.data);
// casting as Person and printing actual property
System.out.println(((Person)wrapped.data).address);
Output (similar to...)
test.Main$Person#dfd3711
23432
Explanation for TypeReference, from the docs:
This generic abstract class is used for obtaining full generics type
information by sub-classing; it must be converted to ResolvedType
implementation (implemented by JavaType from "databind" bundle) to be
used. Class is based on ideas from
http://gafter.blogspot.com/2006/12/super-type-tokens.html, Additional
idea (from a suggestion made in comments of the article) is to require
bogus implementation of Comparable (any such generic interface would
do, as long as it forces a method with generic type to be
implemented). to ensure that a Type argument is indeed given.
Usage is by sub-classing: here is one way to instantiate reference to
generic type List:
TypeReference ref = new TypeReference<List<Integer>>() { };
which can be passed to methods that accept TypeReference, or resolved
using TypeFactory to obtain ResolvedType.
I'm quite new to the Jackson library (version 1.9). I'm using it only since a couple of weeks, and I find it very flexible and time-saving when it's about serializing and deserializing objects in Java.
I'm experiencing troubles, though, into deserializing "flat" JSONs to a class which is a composition of another, when both are meant to be immutable.
My situation is pretty much the following:
class Foo {
private final String var1;
Foo(String var1) {
this.var1 = var1;
}
// getters omitted
}
class A {
private final Foo foo;
private final String var2;
A(/* #JsonUnwrapped doesn't work here */ Foo foo, String var2) {
this.foo = foo;
this.var2 = var2;
}
#JsonUnwrapped
Foo getFoo() {
return foo;
}
String getVar2() {
return var2;
}
}
class B extends Foo {
private final String var2;
B(String var1, String var2) {
super(var1);
this.var2 = var2;
}
// getters omitted
}
And the JSON to deserialize is something like this:
{ "var1" : "some_value", "var2" : "some_other_value" }
The question is: is there an annotation-based way (so, without the need of using a custom deserializer) to tell Jackson to compose the given JSON to a 'A' instance?
I've tried using the #JsonUnwrapped attribute for the Foo argument in class 'A' constructor, but it's not supported in multi-argument constructor as it would need a JsonProperty to work (which doesn't make sense, because there is actually no single property for those items).
Serialization, instead, works perfectly using this pattern.
It would also work with a non-immutable class by using separate setters, but I'd like to know if there's a way to do the same by only using the constructors (or a builder, which would make sense as in reality the fields are much more than the one in the example).
The very same method obviously works with class 'B' which inherits from 'Foo'.
Thanks in advance.
Note that Jackson's deserialization processing doesn't necessarily respect the immutability of final fields. So, a simple approach would be to just provide no-argument (private) constructors for Jackson to use.
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
// {"var1":"some_value", "var2":"some_other_value"}
String jsonInput = "{\"var1\":\"some_value\", \"var2\":\"some_other_value\"}";
ObjectMapper mapper = new ObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
A a = new A(new Foo("some_value"), "some_other_value");
System.out.println(mapper.writeValueAsString(a));
// output: {"var1":"some_value","var2":"some_other_value"}
A aCopy = mapper.readValue(jsonInput, A.class);
System.out.println(mapper.writeValueAsString(aCopy));
// output: {"var1":"some_value","var2":"some_other_value"}
}
}
class Foo
{
private final String var1;
Foo(String var1) {this.var1 = var1;}
private Foo() {this.var1 = null;}
}
class A
{
#JsonUnwrapped
private final Foo foo;
private final String var2;
A(Foo foo, String var2)
{
this.foo = foo;
this.var2 = var2;
}
private A()
{
this.foo = null;
this.var2 = null;
}
}
If you really don't want to provide such (extra) constructors, then it would be nice if a similar solution could be devised using #JsonCreator, but I wasn't able to get such a thing to work. So, I recommend logging an enhancement request at https://github.com/FasterXML/jackson-core/issues, maybe to better support annotating a #JsonCreator argument with both #JsonUnwrapped and #JsonProperty.
Unfortunately there are certain combinations of features that may not be possible to implement properly; and this may be one of those (I am not 100% sure: feel free to file a Bug/RFE for Jackson github issues or Jira). This is because the way #JsonUnwrapped and #JsonCreator both require potential reordering of data; and also because the order of creating actual instance complicates things.
So while conceptually this should be possible, there may be implementation difficulties.
As to Jackson 2.0: I would definitely try it over 1.9 because some parts of #JsonUnwrapped handling have been improved; and any fixes/improvements will be added there. 1.9 branch will get bugfixes backported wherever possible, but no new features will be added.