JAXB marshall/unmarshall objects defined by plugins - java

Let's suppose the following scenario:
I have a list of objects of the following type:
public class MyObject {
private String name
private SomeClass someField
private List<Fact> facts
}
The fields name and someField are just to show that the class has some regular members. You can suppose that it's known how to convert these classes to xml.
Fact is an interface where the implementations are not known to me but provided by plugins. Plugins can be required to provide arbitrary code, but I would like to make it as simple as possible.
I want to save and load these objects to xml. Note that while loading the xml, not all implementations may be present (the xml might have been written with a different set of plugins). I want to be able to still read the xml and not lose any information when saving again. In other words: I'm willing add a field such as List<Element> or List<String> to the class and when reading the xml, all parts where a plugin is present should be read into the corresponding Facts, while all parts without a plugin should be stored in an Element or String and when saving again, both lists get saved and could be read by a program having all plugins.
How best to achieve this using JAXB?
One way I can see is to use Map<Class, org.w3c.dom.Element> instead of List<Fact> which can be converted to xml by JaxB and then let any plugin provide custom code converting from and to "their" element using the org.w3c.dom API, but using that API is somewhat cumbersome, so I wonder whether there is a better way?

No idea about best, but one approach that comes close to what you describe is this:
JAXB doesn't work with interfaces; best it can do would be an abstract class. Meaning you need to use List<Object> or List<AbstractFact>. (but you can enforce some restriction in the getter, pluginresolver or afterUnmarshall()).
Your plugin provides the basic classes for the extension (SPI would be the usual approach). You collect them and (after validation) use them to create your JAXBContext. (If you want to support multiple interfaces, maybe provide them by different methods).
In the xml you need to have a type marker like this: <fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">. If you create the xml with jaxb it will be created autmatically. (The classes need to have the #XmlRootElement annotation).
Here is a stripped down example:
interface Fact {
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
class R {
#XmlElement(name = "fact")
private List<Object> facts;
#SuppressWarnings("unchecked")
public List<Fact> getTest() {
if (facts == null) {
facts = new ArrayList<>();
}
return (List<Fact>) (Object) facts;
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
// check if all facts implement same interface
for(Object object:facts) {
if (!(object instanceof Fact)) {
throw new IllegalArgumentException("Unsupported type in facts list");
}
}
}
}
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "aFact")
class AFact implements Fact {
#XmlElement
private String a;
public AFact() {
}
public AFact(String a) {
this.a = a;
}
public String getA() {
return a;
}
#Override
public String toString() {
return "AFact [a=" + a + "]";
}
}
public class Jax {
public static void main(String[] args) throws JAXBException {
String xml = "<r><fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><a>ba</a></fact></r>";
List<Class<?>> contextClasses = new ArrayList<>();
contextClasses.add(R.class);
contextClasses.addAll(getClassesFromPlugin());
JAXBContext context = JAXBContext.newInstance(contextClasses.toArray(new Class<?>[0]));
R entity = (R) context.createUnmarshaller().unmarshal(new StringReader(xml));
System.out.println(entity.getTest());
R r = new R();
r.getTest().add(new AFact("ab"));
context.createMarshaller().marshal(r, System.out);
}
private static List<Class<?>> getClassesFromPlugin() {
List<Class<?>> asList = Arrays.asList(AFact.class);
for(Class<?> cls:asList) {
if (!Fact.class.isAssignableFrom(cls)) {
throw new IllegalArgumentException("Unsupported class");
}
}
return asList;
}
}

Related

Create Generic class/method to map one object to another

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.

Is it appropriate to use reflection to create a list of objects of all subclasses?

In my program, different features are divided into different modules, potentially hundreds of modules,
each module is a subclass of an abstract Module class
which look something like this
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
with subclasses that look like this
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
#Override
public void execute() {
//Do something
}
}
and I need to make a list of instances of all modules but doing it like this is kinda tedious since my program might have hundreds of modules and it might be hard to debug(I might miss a few lol)
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
MODULES.add(new Mod1());
MODULES.add(new Mod2());
MODULES.add(new Mod3());
MODULES.add(new Mod4());
MODULES.add(new Mod5());
}
so I think using reflection might be the way to go but after a quick google search, I see many people don't like reflection in production code, so I come here to ask if this is an appropriate case to use reflection or is there any reason or design change to avoid using reflection in this particular case
edit: the list will be used stuff like rendering modules in gui or calling execute method of the module from other user interface like command(simply find the module with matching name and execute it)
note: this is not how my code actually look like but a highly simplified version that give a similar idea
For a minimal-effort approach, java provides the built-in class java.util.ServiceLoader.
With this class you can obtain all available implementations of a service class (in this case Module) like
ServiceLoader moduleLoader = ServiceLoader.load(Module.class);
for (Module mod : moduleLoader) {
System.out.println(mod.name);
}
Note that I highlighted the term available in the previous sentence. In fact, the ServiceLoader implementation does not perform any advanced class-hierarchy lookup, instead it relies on a specific resource file named equal to the canonical name of the Module class.
META-INF/services/com.example.project.Module
com.example.project.impl1.Module1
com.example.project.impl2.Module2
Where each line of the file references an available implementation of Module.
Now, ServiceLoader is an implementation of the java SPI specification, which does not necessarily have to qualify perfectly for your use case. However, since its idea of service loading and providing is rather simple, you can easily build your own variant.
public class JsonServiceLoader<S> {
private Class<S> service;
private Map<String, String> serviceIdentifiers;
public static <T> JsonServiceLoader load(Class<T> service, ClassLoader cl) {
Map<String, String> serviceIdentifiers = new HashMap<>();
String name = "META-INF/json-services/" + service.getCanonicalName();
// TODO check for null references where necessary
for (Enumeration<URL> resources = cl.getResources(name); resources.hasMoreElements();) {
try (InputStream resource = resources.next().openStream()) {
for (Map.Entry<String, String> identifier : parseJson(resource).entrySet()) {
serviceIdentifers.merge(
identifer.getKey(),
identifer.getValue(),
(value1, value2) -> throw new ServiceConfigurationError(
"duplicate service identifier '" + identifier.getKey() + "'"
);
);
}
}
}
return new JsonServiceLoader<>(service, serviceIdentifiers);
}
private static Map<String, String> parseJson(InputStream resource) {
// TODO parse JSON data from the given stream using your favourite JSON facility
/*
* If you want to use a different style of resources, e.g. XML, this is the only
* location you have to change (you might want to rename 'json-services' though).
*/
return new HashMap<>();
}
private JsonServiceLoader(Class<S> service, Map<String, String> serviceIdentifiers) {
this.service = service;
this.serviceIdentifiers = serviceIdentifiers;
}
public Set<String> getServiceIdentifiers() {
return Collections.unmodifiableSet(serviceIdentifiers.keySet());
}
public S getService(String name) {
String className = serviceIdenfiers.get(name);
if (null == className) {
throw new IllegalArgumentException("invalid service identifier '" + name + "'");
}
// TODO improve error handling
try {
return service.cast(Class.forName(className).newInstance());
} catch(Exception exc) {
throw new ServiceConfigurationError("could not load service '" + name + "'", exc);
}
}
}
Your JSON resources could look like
META-INF/json-services/com.example.project.Module
{
"Mod1" : {
"class" : "com.example.project.impl1.Module1"
},
"Mod2" : {
"class" : "com.example.project.impl2.Module2"
}
}
allowing future extensions.
At this point, the Module class also does not need to know its name (the class member name) anymore, since you can always ask the service loader for an appropriate instance. (If you do so at some place in your code, you will already know for which name you just asked.) If so desired, you can also add more logic to this JsonServiceLoader, such as caching.
Ultimately, it all depends on how much information you want to process around your Module and how much of this information you want the Module implementations to handle and how much of it you want the service framework to handle.
Perhaps you can pass the list into the constructor of the parent class and add subclass itself to the list in the constructor method.
Just like this
public abstract class Module {
public final String name;
public Module(String name, List<Module> list) {
this.name = name;
list.add(this);
}
public abstract void execute();
}
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
new Mod1(MODULES);
}

How to use java reflection to return a Collection that contains something other then Objects

So my problem is that im currently trying to use java's reflection to traverse a tree like structure. The problem is the only thing i know about each structure is that it can contain one of three things. Strings (the leaf's) Other Objects, Or Lists of other objects. Using reflection i want to do a DFS of the tree until i find a node that im looking for. My problem seems to be that when i use reflection to get a field that happens to be of type List i get back List and i am unable to down cast the the correct type. here are some samples i have tried.
Using Fields
Object returnObj = new Object();
Field field = object.getClass().getDeclaredField(fieldClassName);
field.setAccessible(true);
List<DistributionPartnerRoleType> test = (List<DistributionPartnerRoleType>) field.get(object);
And using Methods
String methodName = "get" + Character.toUpperCase(fieldClassName.charAt(0)) + fieldClassName.substring(1);
Method[] getters = object.getClass().getMethods();
Method getter = getMethod(getters, methodName);
Type returnType = getter.getGenericReturnType();
if(returnType instanceof ParameterizedType){
Type actualType = ((ParameterizedType) returnType).getActualTypeArguments()[0];
Class actualClass = (Class) actualType;
returnObj = getter.invoke(object, null);
List<Object> newList = new ArrayList<Object>();
for(Object obj : (List<Object>)returnObj){
newList.add(actualClass.cast(obj));
}
returnObj = newList;
}
Im aware that the problem is that the objects are truly of type Object but the function and fields are explicitly of type List as declared in the code
protected List<DistributionPartnerRoleType> distributionPartnerRole;
public List<DistributionPartnerRoleType> getDistributionPartnerRole() {
if (distributionPartnerRole == null) {
distributionPartnerRole = new ArrayList<DistributionPartnerRoleType>();
}
return this.distributionPartnerRole;
}
If anyone knows of a solution for this problem that would be great, Or if i need to go about a different method other then reflection.
To sum up my problem. Invoke is returning a List But the objects inside the list are not actually of the type this function returns they are of type java.lang.Object Is there any way to get around this or is dynamic access of the lists objects not possible?
I don't think use of reflection is a good idea in this case. In my view there are very few appropriate use cases. It's often a sign that you need to rethink your approach.
In this case I suggest you look at the Visitor design pattern. The visitor itself can include the logic for a depth-first search as well as dealing with the various types of data in the nodes.
In your case the pattern might look something like:
interface Node {
void accept(NodeVisitor visitor);
}
class StringNode implements Node {
public String getValue();
public void accept(NodeVisitor visitor) {
visitor.visit(this);
}
}
class IntegerNode implements Node {
public int geValue();
public void accept(NodeVisitor visitor) {
visitor.visit(this);
}
}
class CompositeNode implements Node {
public void forEachChild(Consumer<Node> action);
public void accept(NodeVisitor visitor) {
visitor.visit(this);
}
}
interface NodeVisitor {
default void visit(StringNode node) {}
default void visit(IntegerNode node) {}
default void visit(CompositeNode node) {}
}
Now your search algorithm might look like:
class IntegerSearch implements NodeVisitor {
private final int target;
private final List<IntegerNode> results = new ArrayList<>();
void visit(IntegerNode node) {
if (node.getValue() == target)
results.add(node);
}
}
void visit(CompositeNode node) {
node.forEachChild(child -> child.accept(this));
}
}
No reflection, casting or other dodgy idioms!
This might not exactly match your case (e.g. perhaps the nodes with values are also composites?) but hopefully you see the general pattern for avoiding reflection.
There is a deeper problem here, As stated by Louis Wasserman, reflection should not be causing this and there seems to be a problem with my code in another location.

Is this an example of anti-pattern?

Me and one of my colleague were trying to solve the following problem:
Lets take an example of class A
One of my colleagues was facing problem of extracting one particular property from A.
Fetching one property from One particular class (in this case A) is easy. but lets
assume that you have multiple classes (A1, A2...) and you want to fetch one
particular property from the collection of these classes with more and more reusability of code.
for example
public class A {
private String name;
.
.
.
}
List<String> listOfNames = createNameList(listOfAInstances);
createNameList() method would be like following:
List<String> tempList = new ArrayList<>();
for(A a : listOfAInstances) {
tempList.add(a.getName());
}
return tempList;
now if there are multiple classes I have to do this for each class and different properties.
I suggested two approaches:
Reflection based approach.
Create an interface called "PropertyExtractable" and put a method in it called "extractProperty" in it.
As shown below:
interface PropertyExtractable {
Object extractProperty();
}
public class A implements PropertyExtractable {
private String name;
.
.
.
public Object extractProperty() {
return this.name;
}
}
For this I can write some utility method which then can be used everywhere i.e.
public Object getPropertiesOfPropertyExtractable(PropertyExtractable prExtractable) {
return prExtractable.extractProperty();
}
This was the background, one other colleague of mine had different opinion about 2nd approach, he told me it seems like anti-pattern. He tried to explain to me but I didn't get it entirely so and hence I am asking here.
I am trying to compare this example with the Comparator interface in Java. Like java allows us to use Comparator on any of the custom object class and allows us to define the logic for comparison then why can't I define the logic for extraction
Further more interfaces can be used in this way, then why shouldn't we use it
I want to know is this approach an anti-pattern? why?
You can place extracting code in separate method and reuse it:
class A {
private String name;
public String getName() {
return name;
}
}
class B {
private String surname;
public String getSurname() {
return surname;
}
}
public class SomeClass {
private <T> List<String> extractFields(List<T> list, Function<T, String> extractorFunction) {
return list.stream().map(extractorFunction).collect(Collectors.toList());
}
public void someMethod() {
List<A> listOfInstancesA = new ArrayList<>();
List<B> listOfInstancesB = new ArrayList<>();
// fill lists
List<String> fieldsA = extractFields(listOfInstancesA, A::getName);
List<String> fieldsB = extractFields(listOfInstancesB, B::getSurname);
}
}
The situation you describe is working with a legacy system which you don't want to change.
Since if you weren't you'd introduce an interface for the common properties (like your example for the Comparator interface). You introduced an interface without a meaning which may be an anti-pattern since you actually need a functional interface: PropertyExtractable vs. NamedObject=> has a method: String getName()).
If you want to implement Reflection, then your interface may be correct but I don't see it (e.g. in your case you already have Reflection built in into Java).
Usually you use the Adapter pattern to get a property/method from an object which doesn't implement the requested interface.

How to make a JSON representation of a Java class?

Id like to represent a Class object as JSON. For example, if I have the class defintions as follows:
public class MyClass {
String myName;
int myAge;
MyOtherClass other;
}
public class MyOtherClass {
double myDouble;
}
I'd like to get the following nested JSON from a Class object of type MyClass:
{
myName: String,
myAge: int,
other: {
myDouble: double;
}
}
EDIT:
I don't want to serialize instances of these classes, I understand how to do that with GSON. I want to serialize the structure of the class itself, so that given a proprietary class Object I can generate JSON that breaks down the fields of the class recursively into standard objects like String, Double, etc.
With Jettison, you can roll your own mappings from Java to JSON. So in this case, you could get the Class object of the class you want, then map the Java returned by the getFields, getConstructors, getMethods etc. methods to JSON using Jettison.
I would recommend to use Jackson.
You can also take a look at the JSonObjectSerializer class based on Jackson which can be found at oVirt under engine/backend/manager/module/utils (you can git clone the code) and see how we used Jackson there.
Looking to do the same thing, in the end I wound up writing my own method, this does not handle all cases e.g. if one of the declared fields is a Map this will break, but this seems to be alright for most common objects:
#Override
public Map reflectModelAsMap(Class classType) {
List<Class> mappedTracker = new LinkedList<Class>();
return reflectModelAsMap(classType, mappedTracker);
}
private Map reflectModelAsMap(Class classType, List mappedTracker) {
Map<String, Object> mapModel = new LinkedHashMap<String, Object>();
mappedTracker.add(classType);
Field[] fields = classType.getDeclaredFields();
for (Field field : fields) {
if (mappedTracker.contains(field.getType()))
continue;
if (BeanUtils.isSimpleValueType(field.getType())) {
mapModel.put(field.getName(), field.getType().toString());
} else if (Collection.class.isAssignableFrom(field.getType())) {
Class actualType = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
mapModel.put("Collection", reflectModelAsMap(actualType, mappedTracker));
} else {
mapModel.put(field.getName(), reflectModelAsMap(field.getType(), mappedTracker));
}
}
return mapModel;
}
The mapped tracker there because of how I handle relationships in Hibernate; without it there is an endlessly recursive relationship between parent and child e.g. child.getFather().getFirstChild().getFather().getFirstChild().getFather()...

Categories

Resources