Jackson ignore attribute on sub class that is 3:d party - java

I have a 3d library that is included in a class i uses that i want to serialize to json with jackson.
I want to jackson the object A, but ignore the field in Class C without being able to change the source code on Class B and C , is this possible?
class A {
B b;
}
class B {
C c;
}
class C {
int field;
}

I believe you can achieve a solution by using custom serializers.
You can add custom serializers through the ObjectMapper. I have created a small unit test below that demonstrates how it can be achieved:
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.module.SimpleModule;
import org.junit.Test;
import java.io.IOException;
public class JacksonSerializerTest {
#Test
public void test() throws Exception {
C c = new C("initially lowercase string in c");
B b = new B(c);
A a = new A(b);
SimpleModule module = new SimpleModule("MyCustomModule", new Version(1, 0, 0, null));
module.addSerializer(new CustomSerializerForC());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
String pretty = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(a);
System.out.println(pretty);
}
public class A {
private B b;
public A(B b) {
this.b = b;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private C c;
public B(C c) {
this.c = c;
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
}
public class C {
private String value;
public C(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public class CustomSerializerForC extends JsonSerializer<C> {
#Override
public Class<C> handledType() {
return C.class;
}
#Override
public void serialize(C c, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String upperCase = c.getValue().toUpperCase();
jsonGenerator.writeString(upperCase);
}
}
}

Related

Ignoring properties in Jackson if either getter or setter is missing (or is not visible)

I would like ONLY serialization/deserialization of properties that have BOTH getter/setter methods and the methods are PUBLIC. If either the getter or setter method is missing (or is not PUBLIC) then property should be ignore.
In below example properties 'foo' and 'bar' should both be ignored because of missing getter and non-public setter methods, respectively:
class FooBar
{
public FooBar(int foo, int bar)
{
this.foo = foo;
this.bar = bar;
}
public int getFoo()
{
}
public void setBar(int b)
{
this.bar = bar;
}
protected int getBar()
{
return bar;
}
private int foo;
private int bar;
}
Possible to do this using Jackson? Thanks in advance.
Jackson has two mechanisms for filtering unwanted properties. Most commonly used is filtering by annotations (#JsonIgnore, #JsonIgnoreProperties). There are many samples in explaining the usage (e.g. https://springframework.guru/jackson-annotations-json/).
But if you really wish to use a custom filtering of properties based on custom conditions, then you can use filtering (see this post as reference https://www.logicbig.com/tutorials/misc/jackson/json-filter-annotation.html). This way you can build your own rules of serialization.
Here's a sample code that illustrates this technique:
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.stream.Stream;
public class M4 {
private static final String MY_FILTER_NAME = "myFilter";
#JsonFilter(MY_FILTER_NAME)
public static class MyPojo {
private int a;
private int b;
private int c;
public MyPojo(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public void setB(int b) {
this.b = b;
}
public int getC() {
return c;
}
private void setC(int c) {
this.c = c;
}
}
private static class MySimpleBeanPropertyFilter extends SimpleBeanPropertyFilter {
#Override protected boolean include(BeanPropertyWriter writer) {
return super.include(writer);
}
#Override
protected boolean include(PropertyWriter writer) {
String setter = "set" + StringUtils.capitalize(writer.getName());
String getter = "get" + StringUtils.capitalize(writer.getName());
Class<?> declaringClass = writer.getMember().getDeclaringClass();
Method[] methods = declaringClass.getMethods();
return hasPublicMethod(setter, methods) && hasPublicMethod(getter, methods);
}
private boolean hasPublicMethod(String methodName, Method[] methods) {
return Stream.of(methods)
.filter(m -> Objects.equals(m.getName(), methodName))
.anyMatch(m -> Modifier.isPublic(m.getModifiers()));
}
}
private static class MyFilterProvider extends FilterProvider {
#Override public BeanPropertyFilter findFilter(Object filterId) {
return null;
}
#Override public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
if (Objects.equals(filterId, MY_FILTER_NAME)) {
return new MySimpleBeanPropertyFilter();
}
return super.findPropertyFilter(filterId, valueToFilter);
}
}
public static void main(String args[]) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.writer().with(new MyFilterProvider()).writeValue(System.out, new MyPojo(0, 1, 3));
}
}

XML.toString(jsonObject) in thr org.json jar is trimming tailing zero

XML.toString(jsonObject) in the org.json jar is trimming tailing zero.
for example
{
"somekey":300.10,
"somekey1":300.00,
}
Conveting the above json to xml results in
<somekey>300.1</somekey>
<somekey1>300.0</somekey1>
How can I preserve it from trimming that zero
You could put the values to the JSONObject as String instead of double:
final JSONObject jsonObject = new JSONObject();
jsonObject.put("someKey", "300.10" );
jsonObject.put("somekey1", "300.00" );
final String xml = XML.toString(jsonObject);
This will preserve the ending zeros.
You could use jackson XmlMapper and write and register a DuoubleSerializer with the format the suits your needs.
public class XmlTest {
public static class SimpleBean {
Double a;
Double b;
public Double getA() {
return a;
}
public void setA(Double a) {
this.a = a;
}
public Double getB() {
return b;
}
public void setB(Double b) {
this.b = b;
}
public SimpleBean(Double a, Double b) {
super();
this.a = a;
this.b = b;
}
}
public static class DoubleSerializer extends StdSerializer<Double> {
public DoubleSerializer() {
this(null);
}
public DoubleSerializer(Class<Double> t) {
super(t);
}
#Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeRaw(String.format(Locale.ROOT, "%.2f", value));
gen.writeEndObject();
}
}
public static void main(String[] args) throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Double.class, new DoubleSerializer());
xmlMapper.registerModule(module);
System.out.println(xmlMapper.writeValueAsString(new SimpleBean(1.50, 1.60)));
}
}

Jackson mixin: How to Ignore or Rename fields from class as tree

I'm trying to do deep filtering (rename / ignore fields) from class that is represented as a tree.
With Jackson Mixin I can rename or ignore fields from root level. What I want to achieve, is how to do filtering (rename / ignore) on multy levels?
For instance, I have a tree that has two classes. Class A is the root, B is second level as depth in the tree. By applying Jackson Mxiin, I want to filter property a2 from the root A, and property b1 from B.
Class A that represents root class
public class A {
private String a1;
private String a2;
private B b;
public A(String a1, String a2, B b) {
this.a1 = a1;
this.a2 = a2;
this.b = b;
}
public String getA1() {
return a1;
}
public void setA1(String a1) {
this.a1 = a1;
}
public String getA2() {
return a2;
}
public void setA2(String a2) {
this.a2 = a2;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
Class B - Second level depth
public class B {
private String b1;
private String b2;
public B(String b2, String b1) {
this.b1 = b1;
this.b2 = b2;
}
public String getB1() {
return b1;
}
public void setB1(String b1) {
this.b1 = b1;
}
public String getB2() {
return b2;
}
public void setB2(String b2) {
this.b2 = b2;
}
}
Filters
public interface AMixIn {
// Filter for A (implemented to filter second depth as well)
#JsonIgnore
String getA2();
#JsonIgnore
public BMixIn getB();
}
public interface BMixIn {
// Filter for B
#JsonIgnore
public String getB1();
}
Test
public class SecondLevelTest {
// Test
private ObjectMapper mapper = null;
private ObjectWriter writer = null;
#Before
public void setUp() {
// init
mapper = new ObjectMapper();
mapper.setMixIns(ImmutableMap.<Class<?>, Class<?>>of(A.class, AMixIn.class));
writer = mapper.writer().with(SerializationFeature.INDENT_OUTPUT);
}
#Test
public void rename_field_jackson() throws JsonProcessingException {
B b = new B("vb1", "vb2");
A a = new A("va1", "va2", b);
// I want to get this result
// {
// "a1" : "va1",
// "b2" : "vb2"
// }
String json = writer.writeValueAsString(a);
System.out.println(json);
}
}
This should give you the output you're looking for. Change your setUp() to also use BMixIn:
#Before
public void setUp() {
// init
mapper = new ObjectMapper();
mapper.setMixIns(ImmutableMap.of(A.class, AMixIn.class, B.class, BMixIn.class));
writer = mapper.writer().with(SerializationFeature.INDENT_OUTPUT);
}
And change AMixIn to unwrap B
public interface AMixIn {
// Filter for A (implemented to filter second depth as well)
#JsonIgnore
String getA2();
#JsonUnwrapped
public BMixIn getB();
}
Forgetting to register BMixIn caused its #JsonIgnore to never be used. #JsonUnwrapped un-nests b so you get a flat structure.

How to use a Converter and a PropertyMap in the same mapping?

I am trying to specify the mapping of the member list using a Converter but I would like to also use a PropertyMap to map the remaining fields (in the example below it is just the field 'a' that should be mapped to 'aa').
Is it possible to specify all the mappings in the Converter or just in a PropertyMap? If not, how can I use both in the same model mapper?
Thanks.
P.S. I followed this example http://bit.ly/10dqanw to make my Test class.
package org.me.modelmapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.modelmapper.PropertyMap;
import org.modelmapper.TypeToken;
import org.modelmapper.config.Configuration;
import org.modelmapper.spi.MappingContext;
import static org.testng.Assert.*;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class ModelMapperTest {
private ModelMapper modelMapper;
public static class Foo {
private String a;
private List<Bar> b;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public List<Bar> getB() {
return b;
}
public void setB(List<Bar> b) {
this.b = b;
}
}
public static class Bar {
private String c;
public Bar(String c) {
this.c = c;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
public static class FooDTO {
private String aa;
private List<BarDTO> bb;
public String getAa() {
return aa;
}
public void setAa(String aa) {
this.aa = aa;
}
public List<BarDTO> getBb() {
return bb;
}
public void setBb(List<BarDTO> bb) {
this.bb = bb;
}
}
public static class BarDTO {
private String cc;
public String getCc() {
return cc;
}
public void setCc(String cc) {
this.cc = cc;
}
}
public ModelMapperTest() {
}
#Test
public void shouldMapFooToFooDTO() {
Foo foo = new Foo();
foo.setA("aaa");
List<Bar> b = Arrays.asList(new Bar("ccc"), new Bar("ddd"));
foo.setB(b);
modelMapper.createTypeMap(Foo.class, FooDTO.class).setConverter(
new Converter<Foo, FooDTO>() {
#Override
public FooDTO convert(MappingContext<Foo, FooDTO> context) {
List<BarDTO> barDTOs = new ArrayList<BarDTO>();
List<Bar> bars = context.getSource().getB();
TypeToken<List<BarDTO>> token = new TypeToken<List<BarDTO>>() {
};
MappingContext<List<Bar>, List<BarDTO>> c = context.create(bars, token.getType());
barDTOs.addAll(context.getMappingEngine().map(c));
FooDTO fooDTO = new FooDTO();
fooDTO.setBb(barDTOs);
return fooDTO;
}
;
}
);
PropertyMap<Foo, FooDTO> map = new PropertyMap<Foo, FooDTO>() {
#Override
protected void configure() {
map(source.getA(), destination.getAa());
}
};
modelMapper.addMappings(map);
FooDTO fooDTO = modelMapper.map(foo, FooDTO.class);
assertEquals(fooDTO.getAa(), foo.getA());
int i = 0;
for(; i < fooDTO.getBb().size(); i++) {
assertEquals(fooDTO.getBb().get(i), foo.getB().get(i));
}
}
#BeforeMethod
public void setUpMethod() throws Exception {
modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
}
}
Yes it is possible. A Converter will always take precedence over any property mappings. You can certainly specify both, but the Converter is what will be used.

JAXB Serializing Interface to XML Issues (Map<String,ISomeInterface> not working)

I am attempting to serialize an interface to XML using JAXB 2.2.4, but when I have an interface within a Map<> object, it blows up and gives me the error:
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of
IllegalAnnotationExceptions com.test.IInterface2 is an interface, and
JAXB can't handle interfaces. this problem is related to the
following location: at com.test.IInterface2 at public
java.util.Map com.test.Interface1Impl.getI2() at
com.test.Interface1Impl com.test.IInterface2 does not have a no-arg
default constructor. this problem is related to the following
location: at com.test.IInterface2 at public java.util.Map
com.test.Interface1Impl.getI2() at com.test.Interface1Impl
This code has been tested and works if I remove the Map<>, and have even gotten it to work if i use a List<>, but there is something about the Map<> that JAXB doesn't like.
Here is the code I'm running, please let me know if you know of a way to fix this!
package com.test;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({Interface2Impl.class})
public class main
{
/**
* #param args
*/
public static void main(String[] args) {
IInterface1 i1 = new Interface1Impl();
i1.setA("SET A VALUE");
i1.setB("Set B VALUE");
IInterface2 i2 = new Interface2Impl();
i2.setC("X");
i2.setD("Y");
i1.getI2().put("SOMVAL",i2);
String retval = null;
try {
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Interface1Impl.class, Interface2Impl.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(i1, writer);
retval = writer.toString();
} catch (JAXBException ex) {
//TODO: Log the error here!
retval = ex.toString();
}
System.out.println(retval);
}
}
package com.test;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.sun.xml.bind.AnyTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(AnyTypeAdapter.class)
public interface IInterface1
{
Map<String,IInterface2> getI2();
String getA();
String getB();
void setA(String a);
void setB(String b);
void setI2(Map<String,IInterface2> i2);
}
package com.test;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Interface1Impl implements IInterface1
{
Map<String,IInterface2> i2 = new HashMap<String,IInterface2>();
String a;
String b;
public Interface1Impl()
{
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public Map<String,IInterface2> getI2() {
return i2;
}
public void setI2(Map<String,IInterface2> i2) {
this.i2 = i2;
}
}
package com.test;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.sun.xml.bind.AnyTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(AnyTypeAdapter.class)
public interface IInterface2
{
String getC();
String getD();
void setC(String c);
void setD(String d);
}
package com.test;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Interface2Impl implements IInterface2
{
String c;
String d;
public Interface2Impl()
{
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
}
To get the following output you could do the following (see below):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<interface1Impl>
<a>SET A VALUE</a>
<b>Set B VALUE</b>
<i2>
<entry>
<key>SOMVAL</key>
<value>
<c>X</c>
<d>Y</d>
</value>
</entry>
</i2>
</interface1Impl>
I2Adapter
We will use an XmlAdapter to handle the Map<String, IInterface2>. An XmlAdapter is a JAXB mechanism that converts an object that JAXB can't map into one that it can.
package com.test;
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class I2Adapter extends XmlAdapter<I2Adapter.AdaptedI2, Map<String, IInterface2>> {
#Override
public AdaptedI2 marshal(Map<String, IInterface2> v) throws Exception {
if(null == v) {
return null;
}
AdaptedI2 adaptedI2 = new AdaptedI2();
for(Map.Entry<String,IInterface2> entry : v.entrySet()) {
adaptedI2.entry.add(new Entry(entry.getKey(), entry.getValue()));
}
return adaptedI2;
}
#Override
public Map<String, IInterface2> unmarshal(AdaptedI2 v) throws Exception {
if(null == v) {
return null;
}
Map<String, IInterface2> map = new HashMap<String, IInterface2>();
for(Entry entry : v.entry) {
map.put(entry.key, entry.value);
}
return map;
}
public static class AdaptedI2 {
public List<Entry> entry = new ArrayList<Entry>();
}
public static class Entry {
public Entry() {
}
public Entry(String key, IInterface2 value) {
this.key = key;
this.value = value;
}
public String key;
#XmlElement(type=Interface2Impl.class)
public IInterface2 value;
}
}
Interface1Impl
The #XmlJavaTypeAdapter annotation is used to register the XmlAdapter. In this example we will register it on the i2 property.
package com.test;
import java.util.*;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Interface1Impl implements IInterface1 {
Map<String, IInterface2> i2 = new HashMap<String, IInterface2>();
String a;
String b;
public Interface1Impl() {
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
#XmlJavaTypeAdapter(I2Adapter.class)
public Map<String, IInterface2> getI2() {
return i2;
}
public void setI2(Map<String, IInterface2> i2) {
this.i2 = i2;
}
}
For More Information
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html
Below is the rest of your model with the JAXB annotations removed from the non-model classes:
main
package com.test;
import java.io.StringWriter;
import javax.xml.bind.*;
public class main {
/**
* #param args
*/
public static void main(String[] args) {
IInterface1 i1 = new Interface1Impl();
i1.setA("SET A VALUE");
i1.setB("Set B VALUE");
IInterface2 i2 = new Interface2Impl();
i2.setC("X");
i2.setD("Y");
i1.getI2().put("SOMVAL", i2);
String retval = null;
try {
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Interface1Impl.class,
Interface2Impl.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(i1, writer);
retval = writer.toString();
} catch (JAXBException ex) {
// TODO: Log the error here!
retval = ex.toString();
}
System.out.println(retval);
}
}
IInterface1
package com.test;
import java.util.Map;
public interface IInterface1 {
Map<String, IInterface2> getI2();
String getA();
String getB();
void setA(String a);
void setB(String b);
void setI2(Map<String, IInterface2> i2);
}
IInterface2
package com.test;
public interface IInterface2 {
String getC();
String getD();
void setC(String c);
void setD(String d);
}
Interface2Impl
package com.test;
public class Interface2Impl implements IInterface2 {
String c;
String d;
public Interface2Impl() {
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
}

Categories

Resources