Imagine a tree of Java objects, i.e. a top level object of some class which contains some properties which in turn are objects of other classes and so on until we basically reach fundamental types like integer or string, etc. For example:
class Car {
private Door _leftDoor;
private Door _rightDoor;
}
class Door {
private String _name;
}
By serializing a top level object of class Car, I would like to see an XML document, for example, like this:
<object type="Car">
<property type="Door" identifier="_leftDoor">
<object type="Door">
<property type="String" identifier="_name">I am a left door!</property>
</object>
</property>
<property type="Door" identifier="_rightDoor">
<object type="Door">
<property type="String" identifier="_name">I am a right door!</property>
</object>
</property>
</object>
By the way, notice how this fragment could be plugged into some other hierarchy, for example, if Car object would be property of some other parent object.
My question is: what would be the right way to implement this patternwise, structurewise, designwise, architecturewise? I think my question needs some clarification. The first and simple way to implement this that comes to my mind is something similar to Java's conventional toString:
class Car {
...
public Element toElement(Element element) {
Document document = element.getOwnerDocument();
Attr attr;
Element objectElement = document.createElement("object");
element.appendChild(objectElement);
attr = document.createAttribute("type");
attr.setValue(class.getSimpleName());
objectElement.setAttributeNode(attr);
Element propertyElement;
propertyElement = document.createElement("property");
objectElement.appendChild(propertyElement);
attr = document.createAttribute("type");
attr.setValue(_leftDoor.getClass().getSimpleName());
propertyElement.setAttributeNode(attr);
attr = document.createAttribute("identifier");
attr.setValue("_leftDoor");
propertyElement.setAttributeNode(attr);
_leftDoor.toElement(propertyElement);
propertyElement = document.createElement("property");
objectElement.appendChild(propertyElement);
attr = document.createAttribute("type");
attr.setValue(_rightDoor.getClass().getSimpleName());
propertyElement.setAttributeNode(attr);
attr = document.createAttribute("identifier");
attr.setValue("_rightDoor");
propertyElement.setAttributeNode(attr);
_rightDoor.toElement(propertyElement);
return objectElement;
}
}
class Door {
...
public Element toElement(Element element) {
...
}
}
How good is this idea in the sense of adding this kind of XML serialization directly as methods of corresponding classes? Is it good that I've restricted it to have Element as parameter (although I'm kind of forced to do it because of the way Java XML API is designed)? Is it good that I return Element? Do you have any ideas what could be improved from the architectural point of view in this implementation? Any advice is welcome.
I'm aware of the java.beans.XMLEncoder facility, but this is Java specific XML serializer and redundant for me in this case.
Why not use something like XStream? If that's not applicable then I would suggest you use the reflection API to allow you to keep the code that generates the XML separate. Perhaps you could write a generic method using reflection that would do the job rather than writing specific XML serialization code for each class. You could use custom annotations to specify more granular behaviour within your classes if necessary. But it feels like re-inventing the wheel because something like XStream can probably do a lot of that for you.
Related
I have a use case where I have to parse an XML document and fill details in a corresponding fields of an internal model POJO Details.
Let's say, the XML look like following -
<?xml version="1.0" encoding="UTF-8"?>
...
<TagA>
<Child1>
...
</Child1>
<Child2>
...
</Child2>
</TagA>
...
<TagB>
<Child1>
...
</Child1>
<Child2>
...
</Child2>
</TagB>
....
And the internal model POJO looks like following -
public class Details {
...
Set<TagAInfo> tagAInformation;
Set<TagBInfo> tagBInformation;
...
}
The XML has multiple fields like TagAs, TagBs etc.
Current implementation: So currently there is a mapper/parser class(let's say Mapper.java) that calls multiple methods like mapTagAInfo(details, xmlRootElement), mapTagBInfo(details, xmlRootElement) etc. on details(i.e. instance of Details.java) something like following -
public class Mapper {
....
public Details mapInfo(XmlElement xmlRootElement) {
Details details = new Details();
mapTagAInfo(details, xmlRootElement)
mapTagBInfo(details, xmlRootElement)
mapTagCInfo(details, xmlRootElement)
....
return details;
}
private void mapTagAInfo(details, xmlRootElement) {
stp1: Extract <TagA> tag element info using a utility which reads the xml document
stp2: use the stp1 info and convert to internal model POJO TagAInfo and
add to details (details.addTagAInfo(tagAInfo))
}
Question: The current implementation makes the code look very ugly(as in a single class Mapper.java multiple things are happening), so wanted to know if I can use some design pattern to improve the code ? If so, please suggest what design pattern to use and an example explaining the design pattern usage would help a lot.
Update: The project uses Dom4j to read XML. Also, the question was more around handling the responsibilities of the mapper class. As one can see, it has lot of functions like mapTagAInfo, mapTagBInfo etc. Wanted to know if I should delegate the responsibilities to different classes - eg. TagAInfoEnricher, TagBInfoEnricher etc. If yes, then how to do it, what design pattern to use etc. ?
Try Unmarshalling with JAXB.
Examples:
https://howtodoinjava.com/jaxb/jaxb-unmarshaller-example/
https://howtodoinjava.com/jaxb/read-xml-to-java-object/
I usually like using the builder pattern, Lombok #Builder annotation, to create my objects.
#Builder
#Getter
class Details implements Serializable {
Set<TagAInfo> tagAInformation;
Set<TagBInfo> tagBInformation;
public static void mapTagAInfo(details, xmlRootElement) {
...
}
}
The other design pattern I like to use is creating Static method inside the class to construct my objects especially DTOs
Don't know if this helps
I'm trying to use an XmlAdapter to unmarshal XML into a custom collection type (that intentionally does not implement the Collection interface due to a conflicting restriction imposed by another use of the class, which unfortunately fails if the class contains any collections). The difficulty is that the XML nodes that will be placed into the collection are not wrapped in a wrapper element.
Here's what my XML essentially looks like:
<xml>
<foo></foo>
<bar>1</bar>
<bar>2</bar>
</xml>
I can make JAXB unmarshall this to the following POJO:
class Naive {
Foo foo;
List<Bar> bar;
}
However, what I want to do is the following:
class Bars {
// There will be a fixed number of these, known in advance
Bar bar1;
Bar bar2;
}
class BarsAdapter extends XmlAdapter<ArrayList<Bar>, Bars> {
...
}
class Custom {
Foo foo;
#XmlJavaTypeAdapter(BarsAdapter.class) // won't work
Bars bar;
}
As you can see, I wrote an XmlAdapter that wants to adapt the entire list, not the individual elements, but it never gets invoked for unmarshalling. (It does get invoked for marshalling.)
If my XML contained a wrapper around the <bar> elements, then I know how to solve this:
<xml>
<foo></foo>
<wrapper>
<bar>1</bar>
<bar>2</bar>
</wrapper>
</xml>
because then I could annotate the bars field with #XmlElement(id="wrapper") and the adapter would get correctly called. However, the XML schema comes from an RFC and is entirely unchangeable, so I am stuck as to how to proceed. Any ideas appreciated!
I have an XML document that looks something like the following:
Note that I cannot change the schema because it is part of a standard XML Schema (Library of Congress METS).
<amdSec ID="AMDSEC001">
<digiprovMD ID="DMD001">
<mdWrap MDTYPE="OBJECT">
<xmlData>
<object xsi:type="file">
.....
</object>
</xmlData>
</mdWrap>
</digiprovMD>
<digiprovMD ID="DMD001_EVENT">
<mdWrap MDTYPE="EVENT">
<xmlData>
<event xsi:type="event">
.....
</event>
</xmlData>
</mdWrap>
</digiprovMD>
</amdSec>
As you can see, the inner element <mdWrap> can contain elements of different types; in this case they're <event> and <object>, but it isn't constrained to just those two types. Creating two classes (like below), marshals okay, but this doesn't work for unmarshalling.
class ObjectMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "object")
List<MyObject> object; //Wrapped in list to use #XmlElementWrapper
}
class EventMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "event")
List<MyEvent> event; //Wrapped in list to use #XmlElementWrapper
}
What can I do so that JAXB unmarshals the correct "type" of MDWrap?
I think, the best solution in this case is a generating POJO classes using XJC tool.
Download XSD file which describe XML file.
Using XJC tool convert XSD file into POJO classes. If XSD is not correct - fix it.
Make some changes if you need in generated classes.
Use this classes in marshalling / unmarshalling process.
I was able to figure out the solution, and it's much simpler than I initially thought (which speaks to my relative inexperience with XML and JAXB). By creating my MDWrap class in the following way
class MDWrap {
#XmlAnyElement(lax = true)
#XmlElementWrapper(name = "xmlData")
Object wrappedMD;
}
Then MDWrap can contain an object of any type, and will unmarshal properly, as long as the class of which wrappedMD is an instance of is annotated with #XmlRootElement. The trick is to annotate wrappedMD as XmlAnyElement.
The Question
I'm new to Java and spring and I'd like to know how to structure code which marshals different objects from XML and then processes them. I'm converting some code JAVA code from before my time to use spring. I know the way I've approached this this probably, wrong but If someone could offer a few pointers on how to restructure things "the Spring way" that would help alot. I've read a lot of the Spring docs, but I'm finding it hard to apply what is in there to my code.
The Situation
I'm not going to post the whole code tree as even and simple example is a lot of code (which is the problem). So I'll just describe the method.
I've got XML and schemas for two classes CLASSA & CLASSB. I've generated JAVA wrappers using xjc. I've got a JAVA class which is a wrapper for the JAXB marshaller. The wrapper needs to be given the class name and the package name of the class to be marshalled on construction.
public JaxbWrapper(String ClassName, String packageName) throws ClassNotFoundException, InstantiationException,
IllegalAccessException, JAXBException
{
this.packageName = packageName;
// set the context is the expensive operation
setJAXBContext(Class.forName(fullJaxbClassName(ClassName)).newInstance());
// get the schma from preset schema dir
schemaFileName = findSchema(ClassName);
myMarsheller = jc.createMarshaller();
myMarsheller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
}
I've then got one bean for each instance of the JaxBWrapper for both ClassA and ClassB. They are almost identical so only the one for class A is shown.
<bean id="jaxbParser_CLASSA" class="org.test.JAXB.JaxbWrapper"
lazy-init="true" scope="prototype">
<constructor-arg index="0" type="java.lang.String"
value="CLASSA" />
<constructor-arg index="1" type="java.lang.String"
ref="testPackage" />
<property name="encoding" ref="xmlEncoding" />
</bean>
Unhelpfully the XML files I have to process are delivered one at a time in file with a fixed name and a rolling counter (and I cannot change that). So I'm unable to tell what object is in the file from the filename. I've have a tiny utility function which check which the object type is in the file I'm trying to process.
I then have the following function:
public static Object loadXML(String xmlFile, String fileName) throws Exception
{
InputStream iStream = XmlUtils.openFile(xmlFile);
String xmlObjectType = XmlUtils.getObjectTypeFromXML(xmlFile);
// get the right JAXB processor
JaxbWrapper jaxB = myContext.getBean("jaxbParser_" + xmlObjectType , JaxbWrapper.class);
Object obj = jaxB.objectFromXML(iStream, xmlObjectType , fileName);
return obj;
}
So I'm taking the object name and getting the right bean from the context. This means I could use things like Spring's object pool to hold lots of JaxbWrappers for different objects. However the way I've implemented this feels wrong to me. I don't like the myContext.getBean("jaxbParser_" + xmlObjectType method of getting the JaxbWrapper bean.
So my question is: Is there a better way of structuring this code? A few simple pointers would be very much appreciated.
Additional Complexity
This is where things, at the moment, become really unmanageable. Once the marshaling stage has completed I've got a post processing stage where there are several different post processor for each class type. I've got Spring beans for each of these and I'm getting them from the application context using:
myContext.getBean(xmlObjectType + "_" + processorType + "_" + xmlObjectType);
where:
xmlObjectType is a string CLASSA or CLASSB which is set by reading the object type from the file (as above).
processorType is s string which is set from the comand line.
To set the proceesorType I'm doing something like the following when the application starts.
if (line.hasOption("file"))
{
processorType = "FileProcessor";
}
if (line.hasOption("print"))
{
processorType = "PrintProcessor";
}
Again, I don't think this is the right way to do things, but It's the best I've got at the moment :-(.
I guess the more general question is how do JAXB and Spring work together? In the real world I have lots very large and complex CLASSA and CLASSBs. I've got java classes for these generated by xjc. As I'm using xjc I have to use jaxb (I guess). The question is how to do that in the Spring world.
Or can I get rid of JAXB and use a spring component. I'd need to use something else to generate all the classes other than xjc. But I cannot find anything that would perform that task
There is a lot of stuff going on here and I have to agree with #BlaiseDoughan you should be able to do this outside of Spring.
Although Spring does do object pooling (aop) and has some management for throw away objects you should not use it for such. Basically anytime your doing getBean during runtime unless your know what your doing your doing it wrong.
Spring is for managing behavior based singletons.
So what you want to do is think how can make a singleton that does the behavior... In your case you want to make a Factory of JaxbWrapper.class (and I don't mean Spring's special bean Factory).
So something like:
public class JaxbWrapperFactory {
Map<String, JaxbWrapper> pool = new ConcurrentHashMap(); // recommend Guava's Cache
public JaxbWrapper create(String ClassName, String packageName) throws ClassNotFoundException, InstantiationException { }
public Object loadXML(String xmlFile, String fileName) throws Exception {
/// use create from above
}
}
Now in Spring you can only wire JaxbWrapperFactory.
If you want to make different strategies for create JaxbWrapper based on the input xml you could make sort of strategy interface and wire in the implementations. But I don't think you need that complexity.
Basically to boil it down these tips:
Be-aware that singletons and most spring managed beans must be thread safe.
Reserve public static methods for utility only as this leads to evil singletons.
Often times for object creation its best to make your own factory pattern.
You might want to take a look at JAXB's factory support.
I have an object similar to this:
public class Obj {
#XmlElement(name="value")
public Set<Object> values;
}
When marshaling, this is generating an xml like:
<Obj>
<value xsi:type="xs:dateTime" xmlns:xs="http://www.w3.org/2001/XMLSchema">2009-02-14T00:31:30.001+01:00</value>
<value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">test</value>
</Obj>
However, I want to change some of that values (like the date format used for serializing Date and Timestamp objects), and also get rid of the annoying xsi attributes (but this is not really a requirement, I can live with that)
I've tried adding a #XmlJavaTypeAdapter to values, but in the adapter I get the full Set<Object> to adapt, instead of single elements. I've also tried with a package adapter, but, as my Set is for Object, I cannot put the #XmlJavaTypeAdapter(type) attribute.
Also, I've tried with #XmlJavaTypeAdapter(value=MyAdapter.class, type=Timestamp.class) to get only an adapter for the values inside that Object that I want.
So the question is, does someone know a way to get an adapter to work for this? Or maybe, change the date format every time a Date or Timestamp object is serialized?
Thanks in advance!
#XmlJavaTypeAdapter with the type property has to be specified on the package level. When used in this way it indicates that all usages of that type within the specified package are converted using the XmlAdapter. E.g. if you have a package-info.java like
#XmlJavaTypeAdapters({
#XmlJavaTypeAdaptor(type=Timestamp.class, value=MyAdapter.class)
})
package org.example;
Then a class in that package with a Timestamp field.
package org.example;
public class Obj {
public Timestamp aTimestamp;
}
The specified adapter will be used to convert the timestamp. I suspect that this will work for your Set<Object> case but I haven't tried it myself.
The reason for the xsi:type attribute is that JAX-B likes to produce XML it can deserialize, so it needs to indicate what type it is or it could only parse everything back as strings. You can get rid of this attribute by using the #XmlElementRef annotation to create a schema substitution group, but in this case the XML will be produced with different element names. E.g.
public class Obj {
#XmlElementRefs({
#XmlElementRef(type=String.class, name="string"),
#XmlElementRef(type=Timestamp.class, name="timestamp")
})
public Set<Object> value;
}
Would produce the following XML structure if you had a timestamp and a string in the set. In this scenario the xsi:type attribute is unnecessary since JAX-B can tell what type to create from the element name.
<Obj>
<timestamp>2009-02-14T00:31:30.001+01:00</timestamp>
<string>test</string>
</Obj>
I would strongly recommend using the #XmlElementWrapper annotation to wrap up all the set items if you're going to take this approach.
If all you're after is a simple set of strings that you don't care about deserializing back to Java (or any other) objects with the correct types, then the simplest solution is to have an XmlAdapter that does just adapt the full Set<Object> into a Set<String> and handle the conversion yourself.