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.
Related
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));
}
}
I am trying to work with PersistentActor in Akka.
I tried the basic example provided in the Akka documentation at https://doc.akka.io/docs/akka/current/persistence.html.
I am getting the following error at the starting of the actor:
Caused by: java.lang.IllegalArgumentException: Default journal plugin
is not configured, see 'reference.conf' at
akka.persistence.Persistence$.verifyPluginConfigIsDefined(Persistence.scala:193)
at
akka.persistence.Persistence.defaultJournalPluginId$lzycompute(Persistence.scala:228)
at
akka.persistence.Persistence.defaultJournalPluginId(Persistence.scala:226)
at
akka.persistence.Persistence.journalConfigFor(Persistence.scala:336)
at akka.persistence.Eventsourced.$init$(Eventsourced.scala:97) at
akka.persistence.AbstractPersistentActor.(PersistentActor.scala:455)
at
org.spituk.learning.akka.samples.ExamplePersistentActor.(ExamplePersistentActor.java:72)
The code I tried is like:
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.persistence.AbstractPersistentActor;
import akka.persistence.SnapshotOffer;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
class Cmd implements Serializable {
private static final long serialVersionUID = 1L;
private final String data;
public Cmd(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
class Evt implements Serializable {
private static final long serialVersionUID = 1L;
private final String data;
public Evt(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
class ExampleState implements Serializable {
private static final long serialVersionUID = 1L;
private final ArrayList<String> events;
public ExampleState() {
this(new ArrayList<>());
}
public ExampleState(ArrayList<String> events) {
this.events = events;
}
public ExampleState copy() {
return new ExampleState(new ArrayList<>(events));
}
public void update(Evt evt) {
events.add(evt.getData());
}
public int size() {
return events.size();
}
#Override
public String toString() {
return events.toString();
}
}
public class ExamplePersistentActor extends AbstractPersistentActor {
private int snapShotInterval = 1000;
private ExampleState state = new ExampleState();
public static Props props() {
return Props.create(ExamplePersistentActor.class);
}
public int getNumEvents() {
return state.size();
}
#Override
public String persistenceId() {
return "sample-id-1";
}
#Override
public Receive createReceiveRecover() {
return receiveBuilder()
.match(Evt.class, state::update)
.match(SnapshotOffer.class, ss -> state = (ExampleState) ss.snapshot())
.build();
}
#Override
public Receive createReceive() {
return receiveBuilder()
.match(
Cmd.class,
c -> {
final String data = c.getData();
final Evt evt = new Evt(data + "-" + getNumEvents());
System.out.println("Cmd received::" + c);
persist(
evt,
(Evt e) -> {
state.update(e);
getContext().getSystem().getEventStream().publish(e);
if (lastSequenceNr() % snapShotInterval == 0 && lastSequenceNr() != 0)
// IMPORTANT: create a copy of snapshot because ExampleState is mutable
saveSnapshot(state.copy());
});
})
.matchEquals("print", s -> System.out.println(state))
.build();
}
public static void main(String[] args) throws IOException {
ActorSystem persistentSystem = ActorSystem.create("persistent-system");
ActorRef persistentSystemActor = persistentSystem.actorOf(ExamplePersistentActor.props());
persistentSystemActor.tell(new Cmd("Hello"), ActorRef.noSender());
System.in.read();
persistentSystem.terminate();
}
}
I have not defined any configurations for the persistence intend to use the built-in default plugins. Can someone please help me with this?
I had to add the following to the application.conf file:
akka.persistence.journal.plugin = "akka.persistence.journal.leveldb"
akka.persistence.snapshot-store.plugin = "akka.persistence.snapshot-store.local"
akka.persistence.journal.leveldb.dir = "target/example/journal"
akka.persistence.snapshot-store.local.dir = "target/example/snapshots"
# DO NOT USE THIS IN PRODUCTION !!!
akka.persistence.journal.leveldb.native = false
I'm messing around with lambdas and I'm trying to create a generic way to form a predicate for a class on a field. Here's some code to illustrate:
public class A {
private String b;
private String c;
public A(String b, String c) {
this.b = b;
this.c = c;
}
public String getB() {
return b;
}
public String getC() {
return c;
}
}
public class Main {
public static void main(String[] args) {
List<A> list = Arrays.asList(new A("aa","bb"),new A("aaC","bb"));
Test test = new Test();
test.setList(list);
test.createPred("aa");
}
}
public class Test {
private List<A> list;
public void setList(List<A> list) {
this.list = list;
}
public Predicate<A> createPred(String query) {
return new Predicate<A>() {
#Override
public boolean test(A t) {
return t.getB().equals(query);
}
};
}
public List<A> search(Predicate<A> a) {
return list.stream().filter(a).collect(Collectors.toList());
}
}
How can I write createPred so it can take a field? I want the method to be "field-generic" I suppose. I'm guessing using java reflection here is not a good idea.
You can make your method take a Function and a T as the query field.
public static void main(String[] args) {
List<A> list = Arrays.asList(new A("aa", "bb"), new A("aaC", "bb"));
Test test = new Test();
test.setList(list);
test.createPred("aa", A::getB);
}
public static class Test {
private List<A> list;
public void setList(List<A> list) {
this.list = list;
}
public <T> Predicate<A> createPred(T query, Function<A, T> f) {
return new Predicate<A>() {
#Override
public boolean test(A x) {
return f.apply(x).equals(query);
}
};
}
public List<A> search(Predicate<A> a) {
return list.stream().filter(a).collect(Collectors.toList());
}
}
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);
}
}
}
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;
}
}