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

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;
}
}

Related

Unable to set Pojo Object variable with an external value during JaxB MoXY Unmarshalling

I am trying the above approach that you have mentioned for NodeAdaptar, but I am getting null for my element.
Scenario:
I have to set the value of variable defined in my Java Model(POJO) Class with a value, coming externally(not part of the input XML). This value will be used in some calculation post unmarshal.
input XML: its a huge one. I am able to unmarshall it correctly.
POJO Class:
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="root")
#XmlAccessorType(XmlAccessType.FIELD)
public class MainModelClass {
#XmlPath("abc/def/Result/text()")
#XmlElement(name="Result")
private String result;
#XmlPath("/abc/def/Score/text()")
#XmlElement(name="Score")
private String score;
///This is where I want to populate the external value ///
#XmlElement
#XmlJavaTypeAdapter(PCNDateAdaptar.class)
private PCNDate pcnDateObject;
public PCNDate getPcnDateObject() {
return pcnDateObject;
}
public void setPcnDateObject(PCNDate pcnDateObject) {
this.pcnDateObject = pcnDateObject;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getScore() {
return score;
}
public void setScore(String score) {
this.score = score;
}
// Block for overridden toString() //
}
Adaptar Class:
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class PCNDateAdaptar extends XmlAdapter<Integer, PCNDate> {
private PCNDate pcnDateObject;
public void setPcnDateObject(PCNDate pcnDateob) {
this.pcnDateObject = pcnDateob;
}
#Override
public PCNDate unmarshal(Integer v) throws Exception {
if(v == null)
return this.pcnDateObject;
else
return this.pcnDateObject;
}
#Override
public Integer marshal(PCNDate v) throws Exception {
if(v == null)
return null;
else
return null;
}
}
PCNDate.class:
public class PCNDate {
private Integer processControlDate;
public Integer getProcessControlDate() {
return processControlDate;
}
public void setProcessControlDate(Integer pcn) {
this.processControlDate = pcn;
}
}
Unmarshaller Method:
public static <T> T getXMLSnippetObject(String xmlString, Class<T> modelClass, XmlAdapter<?, ?> xmlAdapterObject) throws XMLStreamException, JAXBException {
JAXBContext context = JAXBContext.newInstance(modelClass);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setAdapter(xmlAdapterObject);
InputStream xmlInputStream = IOUtils.toInputStream(xmlString);
XMLInputFactory2 xmlInputFactory = (XMLInputFactory2)XMLInputFactory.newInstance();
xmlInputFactory.setProperty("javax.xml.stream.isCoalescing", true);
xmlInputFactory.setProperty("javax.xml.stream.isNamespaceAware", true);
xmlInputFactory.setProperty("javax.xml.stream.isReplacingEntityReferences", true);
xmlInputFactory.setProperty(XMLInputFactory2.P_AUTO_CLOSE_INPUT, true);
//xmlInputFactory.setProperty(XMLInputFactory2.P_DTD_OVERRIDE, false);
xmlInputFactory.setProperty(XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, false);
xmlInputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION,false);
xmlInputFactory.setProperty(XMLInputFactory2.P_INTERN_NS_URIS, true);
XMLStreamReader2 xmlStreamReader = (XMLStreamReader2) xmlInputFactory.createXMLStreamReader(xmlInputStream);
T objectInstance = (T) JAXBIntrospector.getValue(unmarshaller.unmarshal(xmlStreamReader, modelClass));
return objectInstance;
}
Main Calling Class:
public class XMLparsing {
public static void main(String[] args) throws XMLStreamException, JAXBException, IOException {
String xmlString = // Xml String //
MainModelClass modelObj = null;
Integer pcnDate = 20171010;
// PCNDate & PCNDateAdapter are both no-arg-constructor classes for JAXB purpose
PCNDate pcnObj = new PCNDate();
pcnDateObj.setProcessControlDate(pcnDate);
PCNDateAdaptar pcndateAdaptar = new PCNDateAdaptar();
pcndateAdaptar.setPcnDateObject(pcnObj);
modelObj = XmlUtilsStAX.getXMLSnippetObject(xmlString, MainModelClass.class, pcndateAdaptar);
}
Result:
The whole Xml String is getting correctly parsed. Only MainModelClass's pcnDateObject is null, result & score have values. I want pcnDateObject to have 20171010.
I don't know, what I am missing, please help.

Jaxb with baseclass doesn't serialize derived elements

I have a list of modules in a tree like structure. The baseclass contains the childs and some generic property, while the derived class should provide it's individual fields. Now when I try to serialize this with JAXB I get only the members of the baseclass but not the ones from the derived classes.
Application.java
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Application
{
public static void main(String[] args)
{
try
{
Emulators emulators = new Emulators();
emulators.addChild(new VICEModule());
emulators.addChild(new VICEModule());
StringWriter sw = new StringWriter();
JAXBContext jaxbContext = JAXBContext.newInstance(emulators.getClass());
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Pretty print
jaxbMarshaller.marshal(emulators, sw);
String s = sw.toString();
System.out.println(s);
}
catch(Throwable e)
{
System.err.println("Exception:"+e);
}
}
}
ModuleBase.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import com.sun.xml.internal.txw2.annotation.XmlAttribute;
public class ModuleBase
{
private List<ModuleBase> mChilds = new ArrayList<>();
private String mModuleId;
private String mModuleName;
public ModuleBase(String oModuleId, String oModuleName)
{
setModuleId(oModuleId);
setModuleName(oModuleName);
}
public void addChild(ModuleBase oModuleNode)
{
mChilds.add(oModuleNode);
}
#XmlElement(name="ModuleList")
public List<ModuleBase> getChildModules()
{
return mChilds;
}
#XmlAttribute
public String getModuleId()
{
return mModuleId;
}
public void setModuleId(String oModuleId)
{
mModuleId = oModuleId;
}
#XmlAttribute
public String getModuleName()
{
return mModuleName;
}
public void setModuleName(String oModuleName)
{
mModuleName = oModuleName;
}
}
Emulators.java
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Emulators")
public class Emulators
extends ModuleBase
{
public Emulators()
{
super("IdEmu129872q53", "Emulators");
}
}
VICEModule.java
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement(name = "VICEModule")
public class VICEModule
extends ModuleBase
{
private String mInstallationPath;
private int mPort;
public VICEModule()
{
super("Id123456", "Vice");
mPort = 6510;
}
//#XmlElement(name="InstallationPath")
#XmlValue
public String getInstallationPath()
{
return mInstallationPath;
}
public void setInstallationPath(String oPath)
{
mInstallationPath = oPath;
}
//#XmlElement(name="Port")
#XmlValue
public int getPort()
{
return mPort;
}
public void setPort(int nPort)
{
mPort = nPort;
}
}
Now when I serialize it, I get the following XML where the VICEModule values are missing and also the module is listed as ModuleList instead of VICEModule and the base fields are put as tags instead of attributes:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators>
<ModuleList>
<moduleId>Id123456</moduleId>
<moduleName>Vice</moduleName>
</ModuleList>
<ModuleList>
<moduleId>Id123456</moduleId>
<moduleName>Vice</moduleName>
</ModuleList>
<moduleId>IdEmu129872q53</moduleId>
<moduleName>Emulators</moduleName>
</Emulators>
So what this should look like is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators ModuleId="IdEmu129872q53" ModuleName="Emulators">
<VICEModule ModuleId="Id1" ModuleName="Name">
<InstallationPath>Path1</InstallationPath>
<Port>6510</Port>
</VICEModule>
<VICEModule ModuleId="Id2" ModuleName="Name">
<InstallationPath>Path2</InstallationPath>
<Port>6511</Port>
</VICEModule>
</Emulators>
When I use the VICEModule as the baseclass, then the XML looks more like it (still without the attributes though).
Writing this small demo app helped, because now I could more easily play around with the marshalling and finally got it working. I'm posting the changes here, to make this sample complete in case somebody needs it as a reference. Emulators.java was not changed, so it's not listed in the answer.
Short answer:
I had to add #XmlAnyElement(lax=true) to my getter for the child modules in order to convert the resulting XML node into a named node as I want it to. Additionally in the main application I had to traverse the tree and collect all classes that are used, so that I can pass it to the JAXBContext.
Application.java
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Application
{
public static List<Class<?>> createClasses(ModuleBase oModule)
{
List<Class<?>> classes = new ArrayList<>();
classes.add(oModule.getClass());
for(ModuleBase module : oModule.getChildModules())
{
List<Class<?>> cls = createClasses(module);
classes.addAll(cls);
}
return classes;
}
public static void main(String[] args)
{
try
{
Emulators emulators = new Emulators();
emulators.addChild(new VICEModule("Id1", "VICE V1", "V1 Path", 6510));
emulators.addChild(new VICEModule("Id2", "VICE V2", "V2 Path", 6511));
StringWriter sw = new StringWriter();
List<Class<?>> classes = createClasses(emulators);
Class<?>[] cls = new Class<?>[classes.size()];
for(int i = 0; i < classes.size(); i++)
cls[i] = classes.get(i);
JAXBContext jaxbContext = JAXBContext.newInstance(cls);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Pretty print
jaxbMarshaller.marshal(emulators, sw);
String s = sw.toString();
System.out.println(s);
}
catch(Throwable e)
{
System.err.println("Exception:"+e);
}
return;
}
}
ModuleBase.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
public class ModuleBase
{
private List<ModuleBase> mChilds = new ArrayList<>();
private String mModuleId;
private String mModuleName;
public ModuleBase(String oModuleId, String oModuleName)
{
setModuleId(oModuleId);
setModuleName(oModuleName);
}
public void addChild(ModuleBase oModuleNode)
{
mChilds.add(oModuleNode);
}
#XmlAnyElement(lax=true)
public List<ModuleBase> getChildModules()
{
return mChilds;
}
#XmlAttribute
public String getModuleId()
{
return mModuleId;
}
public void setModuleId(String oModuleId)
{
mModuleId = oModuleId;
}
#XmlAttribute
public String getModuleName()
{
return mModuleName;
}
public void setModuleName(String oModuleName)
{
mModuleName = oModuleName;
}
}
VICEModule.java
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "VICEModule")
public class VICEModule
extends ModuleBase
{
private String mInstallationPath;
private int mPort;
public VICEModule()
{
super("Id123456", "Vice");
mPort = 6510;
}
public VICEModule(String oId, String oName, String oPath, int nPort)
{
super(oId, oName);
setInstallationPath(oPath);
setPort(nPort);
}
#XmlElement(name="InstallationPath")
public String getInstallationPath()
{
return mInstallationPath;
}
public void setInstallationPath(String oPath)
{
mInstallationPath = oPath;
}
#XmlElement(name="Port")
public int getPort()
{
return mPort;
}
public void setPort(int nPort)
{
mPort = nPort;
}
}
Now the XML renders exactly as intended with storing all the objects from derived classes.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators moduleId="IdEmu129872q53" moduleName="Emulators">
<VICEModule moduleId="Id1" moduleName="VICE V1">
<InstallationPath>V1 Path</InstallationPath>
<Port>6510</Port>
</VICEModule>
<VICEModule moduleId="Id2" moduleName="VICE V2">
<InstallationPath>V2 Path</InstallationPath>
<Port>6511</Port>
</VICEModule>
</Emulators>

JAXB How To Handle Attribute with Colon (:) in tag? --> cim:ACLineSegment

I am trying to unmarshal a CIM file with JAXB. When any tag contains :(colon), the result is always null. If i remove the colons, then it works well.
An example of tags in my file:
Thank you.
<cim:ACLineSegment>
<cim:ACLineSegment.r>2.200000</cim:ACLineSegment.r>
<cim:ACLineSegment.x>68.200000</cim:ACLineSegment.x>
<cim:ACLineSegment.bch>0.0000829380</cim:ACLineSegment.bch>
</cim:ACLineSegment>
Java code here:
package JAXB_read;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="cimACLineSegment")
#XmlAccessorType(XmlAccessType.FIELD)
public class AClineSegment {
#XmlElement(name ="cimACLineSegment.r")
private double resistance;
#XmlElement(name ="cimACLineSegment.x")
private double reactance;
#XmlElement(name ="cimACLineSegment.bch")
private double susceptance;
public double getResistance() {
return resistance;
}
public void setResistance(double resistance) {
this.resistance = resistance;
}
public double getReactance() {
return reactance;
}
public void setReactance(double reactance) {
this.reactance = reactance;
}
public double getSusceptance() {
return susceptance;
}
public void setSusceptance(double susceptance) {
this.susceptance = susceptance;
}
public AClineSegment(double resistance, double reactance, double susceptance) {
super();
this.resistance = resistance;
this.reactance = reactance;
this.susceptance = susceptance;
}
public AClineSegment() {
super();
}
}
package JAXB_read;
import java.io.File;
import javax.xml.bind.*;
public class AClineSegementJAXB {
public void unmarshall()
{
try {
JAXBContext jc = JAXBContext.newInstance(AClineSegment.class);
Unmarshaller ums = jc.createUnmarshaller();
AClineSegment ACline = (AClineSegment)ums.unmarshal(new File("src\\data\\AclineSegment.txt"));
System.out.println("Informations:");
System.out.println("----------------------");
System.out.println("Resistance: "+ACline.getResistance());
System.out.println("Reactance: "+ACline.getReactance());
System.out.println("Susceptance: "+ACline.getSusceptance());
} catch (Exception e) {
System.out.println(""+e.getMessage());
}
}
}
package Main;
import JAXB_read.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
// AC LINE SSEGMENT
public class Main {
public static void main(String[] args)
{
AClineSegementJAXB object = new AClineSegementJAXB();
object.unmarshall();
}
}

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.

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

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);
}
}
}

Categories

Resources