My applications needs to convert data between Java and XML.
When converting the data, I need to distinguish whether or not the value was present, the value was set explicitly to null or the value had a value.
XML example:
<person><name>Bob</name></person> <-- element 'name' contains value "Bob"
<person><name nil="true"/></person> <-- element 'name' was set explicitly to 'nil'/null
<person></person> <-- element 'name' is missing
As Java types like 'String' only knows two states (null or not null), I tried to use Java Optionals to solve this.
A mapping between XML and Java Optionals could look like this:
<person></person> <=> Optional<String> name = null;
<person><name>Bob</name></person> <=> Optional<String> name = Optional.of("Bob");
<person><name nil="true"/></person> <=> Optional<String> name = Optional.empty();
I tried to use JAXB for the marshalling and unmarshalling. The idea was that the setter of a field only gets invoked when a value needs to be set explicitly to an value. That means that a value is absent implicitly.
I had a look on other stackoverflow questions like the following, but all of them were incomplete handling the behaviour I need to achieve:
How to generate JaxB-Classes with java.util.Optional?
Using generic #XmlJavaTypeAdapter to unmarshal wrapped in Guava's Optional
Using Guava's Optional with #XmlAttribute
I've been struggling with this problem for two days now. I tried to use the XMLAdapter and GenericAdapter, tried several ways how to annotate the fields and getter/setter with #XmlElement, tried to use #XmlAnyElment with and without lax, but all of them only led to a partial success. Either the nil value was not handeld correctly, the lists were not printed out correctly, ...
I think every Java webservice with a properly implemented patch operation should have had this problem. (not talking about the "json patch approach" (RFC 6902))
Is there a common way to solve my problem?
The following code is able to distinguish empty name from null name. To make the solution work, I created a PersonList element to contain all of the person elements. Each Person contains a Name that will have isNil() return true if the element was explicitly set to null by the XML:
Person.java:
import java.util.Optional;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlType(propOrder = {"name"})
#XmlRootElement(name = "person")
public class Person {
private Optional<Name> optionalName;
public Person() {
optionalName = Optional.<Name>empty();
}
public Optional<Name> getOptionalName() {
return optionalName;
}
public Name getName() {
return (optionalName.isPresent()) ? (optionalName.get()) : (null);
}
#XmlElement(name = "name", required = false)
public void setName(Name name) {
optionalName = Optional.ofNullable(name);
}
#Override
public String toString() {
return String.format("Person(optionalName.isPresent() = %s, name = %s)",
Boolean.toString(optionalName.isPresent()),
((getName() == null) ? ("null") : (getName().toString())));
}
}
Name.java:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "name")
public class Name {
#XmlAttribute(name = "nil")
private boolean nil;
#XmlValue
private String value;
public Name() {
nil = false;
value = null;
}
public boolean isNil() {
return nil;
}
public void setNil(boolean torf) {
this.nil = torf;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return String.format("Name(nil = %s, value = %s)",
Boolean.toString(nil),
(value == null) ? ("null"):("\""+getValue()+"\""));
}
}
PersonList.java:
import java.util.Iterator;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "PersonList")
public class PersonList {
private List<Person> persons;
public PersonList() {
persons = null;
}
#XmlElement(name = "person")
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder("PersonList(persons = ");
if(persons == null) {
sb.append("null");
}
else {
sb.append("[");
Iterator<Person> iterator = persons.iterator();
while(iterator.hasNext()) {
sb.append(iterator.next().toString());
if(iterator.hasNext()) {
sb.append(", ");
}
}
sb.append("]");
}
sb.append(")");
return sb.toString();
}
}
Main class to demonstrate the solution:
import java.io.ByteArrayInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class XmlOptional {
public static final int STATUS_OKAY = 0;
public static final int STATUS_ERROR = -1;
public static final String XML_DATA = "<PersonList>" +
"<person><name>Bob</name></person>" +
"<person><name nil=\"true\" /></person>" +
"<person></person>" +
"</PersonList>";
private XmlOptional() {
}
private static PersonList loadXml() {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(XML_DATA.getBytes());
JAXBContext context = JAXBContext.newInstance(PersonList.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
PersonList personList = (PersonList)unmarshaller.unmarshal(bais);
return personList;
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
int status = STATUS_OKAY;
try {
PersonList personList = loadXml();
System.out.format("Xml contained: %s%n", personList);
}
catch (Throwable thrown) {
status = STATUS_ERROR;
thrown.printStackTrace();
}
finally {
System.exit(status);
}
}
}
Sample output:
Xml contained: PersonList(persons = [Person(optionalName.isPresent() = true, name = Name(nil = false, value = "Bob")), Person(optionalName.isPresent() = true, name = Name(nil = true, value = "")), Person(optionalName.isPresent() = false, name = null)])
Since I was not able to solve the problem completely by solely using and configuring JAXB properly, I decided to solve it as follows:
(The main goal was to write a subsystem to communicate with an external system based on XML)
As a starting point, I used the XSD schema provided by the target system to communicate with and generated the corresponding (XML)Java classes using JAXB and the XSD file. All the fields in those generated classes were of type JAXBElement<>, in order to be able to hold the 3 states needed (absent, null, someValue).
On the business model side, I used Java classes with Optional<> field types in order to hold the 3 states.
For the mapping, I wrote a mapper which uses reflection to recursively map from JAXB to Java and vice versa. When mapping from Java to JAXB, the mapper used the ObjectFactory to create the JAXBElement objects. (Mapper itself just had about 300 lines of code).
The fields were mapped based on the matching field names.
The most ugly and challenging part was, that the XSD schema file needed to be altered, in order to make JAXB generated classes that uses JAXBElement field types. Therefore I had to manually add the attribute minOccurs="0" nillable="true" to the XML elements, if not already set.
With that solution above, I finally managed to map the XML to Java and vice versa considering the 3 states needed, easily.
Of course, this solution has its drawbacks.
One is the manual modification of the XSD file. Usually bad practice to alter the XSD file provided by the external system, which acts as an interface contract.
For my requirements at the time, the solution worked perfectly. Even changes to the interface contract of the external system could be implemented very easily.
You can use some validation in your java class like #NotNull, #Size and so on. Or you can put default value , to be sure , that it will be not null. After that you can create DTOs (Data transfer object) with the recommended Xml annotations and mapped it with the ModelMapper.
Related
I am trying to get the name of variable in android using java.
The variable has a annotation, and I want to get the variable's name with the annotation's name. is this possible?
just like this,
#getnameofthisfield
private String name;
use getnameofthisfield and get name
You can do it like this:
Class<YourClass> clazz = // somehow get a reference to the class that contains the field
Field[] fields = clazz.getDeclaredFields();
List<String> fieldNames = new LinkedList<>();
for (Field field : fields) {
if (field.isAnnotationPresent(#getnameofthisfield.class)) {
fieldNames.add(field.getName);
}
}
In the end fieldNames will contain the names of all fields, annotated with #getnameofthisfield.
This comes up when you have a Data holder class that is a model for Firebase fields (for example) and the spelling of the member names must exactly equal the Strings in the Firebase tree. While I have not eliminated the duplicate typing/spelling of the Strings/fields, this will at least detect these programming errors at run-time.
public class User {
private String email;
private String name;
// avoid out-of-sync String names of fields in other files
public static String getFieldName(String fieldRequest) {
try {
return User.class.getDeclaredField(fieldRequest).getName();
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
throw new RuntimeException("Unrecognized field in "
+ User.class.getSimpleName() + ", (" + fieldRequest + ")"); }
}
Here is an example usage:
// demonstration of how the getFieldName() protects against mistakes...
String userNameField = User.getFieldName("name"); // this works
String userEmailField = User.getFieldName("userEmail"); // this throws an error
Get annotation value
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class Util{
#SuppressWarnings("unchecked")
public static<T> T getAnnotationValue(Class<?> clazz,Class<? extends Annotation> annotationClass,String element) throws Exception {
Annotation annotation = clazz.getAnnotation(annotationClass);
Method method = annotationClass.getMethod(element,(Class[])null);
if (annotation == null)
return((T)method.getDefaultValue());
return((T)method.invoke(annotation,(Object[])null));
}
}
In my understanding that isnt possible, the java compiler doesn't save variable names. What is it that your trying to do with such name?
I need to make sure that no object attribute is null and add default value in case if it is null. Is there any easy way to do this, or do I have to do it manually by checking every attribute by its getters and setters?
You can use reflection to iterate over the object's field, and set them. You'd obviously need some sort of mapping between types or even field names and required default values but this can be done quite easily in a loop. For example:
for (Field f : obj.getClass().getFields()) {
f.setAccessible(true);
if (f.get(obj) == null) {
f.set(obj, getDefaultValueForType(f.getType()));
}
}
[Update]
With modern Java, you can use annotations to set the default values for fields on a per class basis. A complete implementation might look like this:
// DefaultString.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface DefaultString {
String value();
}
// DefaultInteger.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface DefaultInteger {
int value();
}
// DefaultPojo.java:
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class DefaultPojo {
public void setDefaults() {
for (Field f : getClass().getFields()) {
f.setAccessible(true);
try {
if (f.get(this) == null) {
f.set(this, getDefaultValueFromAnnotation(f.getAnnotations()));
}
} catch (IllegalAccessException e) { // shouldn't happen because I used setAccessible
}
}
}
private Object getDefaultValueFromAnnotation(Annotation[] annotations) {
for (Annotation a : annotations) {
if (a instanceof DefaultString)
return ((DefaultString)a).value();
if (a instanceof DefaultInteger)
return ((DefaultInteger)a).value();
}
return null;
}
}
// Test Pojo
public class TestPojo extends DefaultPojo {
#DefaultString("Hello world!")
public String stringValue;
#DefaultInteger(42);
public int integerValue;
}
Then default values for a TestPojo can be set just by running test.setDetaults()
You need to manually filter input to constructors and setters. Well... you could use reflection but I wouldn't advise it. Part of the job of constructors and setters is to validate input. That can include things like:
public void setPrice(double price) {
if (price < 0.0d) {
throw new IllegalArgumentException("price cannot be negative " + price);
}
this.price = price;
}
and
public void setName(String name) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
}
You could use wrapper functions for the actual check and throwing the exception.
Non-reflective solution for Java 8, without using a series of if's, would be to stream all fields and check for nullness:
return Stream.of(id, name).allMatch(Objects::isNull);
This remains quite easy to maintain while avoiding the reflection hammer.
This will return true for null attributes.
Maybe check Hibernate Validator 4.0, the Reference Implementation of the JSR 303: Bean Validation.
This is an example of an annotated class:
public class Address {
#NotNull
private String line1;
private String line2;
private String zip;
private String state;
#Length(max = 20)
#NotNull
private String country;
#Range(min = -2, max = 50, message = "Floor out of range")
public int floor;
...
}
For an introduction, see Getting started with JSR 303 (Bean Validation) – part 1 and part 2 or the "Getting started" section of the reference guide which is part of the Hibernate Validator distribution.
You can create a function that returns a boolean value and checks every attribute. You can call that function to do the job for you.
Alternatively, you can initialize the object with default values. That way there is no need for you to do any checking.
I don't have enough context to give you a correct answer, but I'll suggest you to make you code immutable as much as possible. Use public final fields. No more getters or setters : every field has to be defined by the constructor. Your code is shorter, more readable and prevents you from writing code with side effects.
It doesn't prevent you from passing null arguments to your constructor though... You can still check every argument as suggested by #cletus, but I'll suggest you to throw IllegalArgumentException instead of NullPointerException that doesn't give no new hint about what you've done.
Anyway, that's what I do as much as I can and it improved my code (readability, stability) to a great extend. Everyone in my team does so and we are very happy with that. We learned that when we try to write some erlang code where everything is immutable.
Hope this helps.
I tried this and it works without any issues to validate if the field is empty.
I have answered your question partially as I haven't personally tried to add default values to attributes
if(field.getText()!= null && !field.getText().isEmpty())
Hope it helps
This is not to check for null, instead this will be helpful in converting an existing object to an empty object(fresh object). I dont know whether this is relevant or not, but I had such a requirement.
#SuppressWarnings({ "unchecked" })
static void emptyObject(Object obj)
{
Class c1 = obj.getClass();
Field[] fields = c1.getDeclaredFields();
for(Field field : fields)
{
try
{
if(field.getType().getCanonicalName() == "boolean")
{
field.set(obj, false);
}
else if(field.getType().getCanonicalName() == "char")
{
field.set(obj, '\u0000');
}
else if((field.getType().isPrimitive()))
{
field.set(obj, 0);
}
else
{
field.set(obj, null);
}
}
catch(Exception ex)
{
}
}
}
I have a domain object in my JAXB hierarchy which must be represented as comma separated value text. Unfortunately, explicitly constructing the CSV String is incredibly costly so that is not an option.
I created a custom #XmlJavaTypeAdapter that returned a DataHandler (as per supported data types) but that always writes the data out in BASE64... but I have a legacy API to preserve that expects the ASCII string in there. Changing the MIME of the DataHandler doesn't change the encoding, but it would impact the XSD's definition of the object contained within.
Is there any way to setup DataHandler (or any other supported Java type) to return the un-encoded String from a streaming input?
I also considered returning an Object (which was really a CharacterData) but that needs to implement public String getData()... requiring me to explicitly construct the String that I'm trying to stream.
In case no one comes up with DataHanler-related solution... The following is just an alternative idea for a "work-around" which does not involve DataHandler. It requires access to the marshaller.
Modify your XML type adapter to not return the content but a kind of short address to get hold of the streaming data (e.g. a file name).
Define a XMLStreamWriter wrapper like here: JAXB marshalling XMPP stanzas. Overwrite the writeStartElement and writeCharacters to intercept the startElement invocation of the CSV element and the immediately following writeCharacters.
The data passed to that specific invocation of writeCharacters will be the address to get hold of the streaming data. Stream it in chunks to the wrapped XMLStreamWriter's writeCharacters.
I don't quite understand why explicitly constructing the CSV string (using StringBuilder) would be more costly than using JAXB builtins.
If the performance is your limiting factor, then I think you should consider creating custom serializers (StringBuilder based, for example) and SAX handlers to parse the XML.
If you have the luxury of changing the protocol, then you might want to check out Grizzly framework, Avro and Google ProtoBuf - there's quite a bit more maintenance with them, but if you are going after performance then these should be faster.
As always, you should do A/B performance tests using both methods before setting anything into stone ;)
Back to the original topic, here's an example on how to use custom adapters:
import static org.junit.Assert.assertEquals;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.junit.Test;
public class Example
{
public String serialize( DataObject d ) throws JAXBException {
StringWriter buffer = new StringWriter();
JAXBContext.newInstance(DataObject.class).createMarshaller().marshal(d, buffer);
return buffer.toString();
}
#Test
public void testSerialize( ) throws JAXBException {
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><dataObject>"
+ "<FirstField>field1 content with special characters &<>'\"</FirstField>"
+ "<Second><!CDATA[[ <!-- now we're just nasty --> ]]></Second>"
+ "<Custom>a,b,c</Custom></dataObject>";
assertEquals(expected, serialize(new DataObject()).replaceAll("(\r)?\n(\r)?", "\n"));
}
}
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
class DataObject
{
#XmlElement( name = "FirstField" )
private final String field1 = "field1 content with special characters &<>'\"";
#XmlElement( name = "Second" )
private final String field2 = "<!CDATA[[ <!-- now we're just nasty --> ]]>";
#XmlElement( name = "Custom" )
#XmlJavaTypeAdapter( value = CustomAdapter.class )
// you can move this over the type
private final CustomType type = new CustomType("a", "b", "c");
}
#XmlAccessorType( XmlAccessType.FIELD )
class CustomType
{
private final String a;
private final String b;
private final String c;
public CustomType( String a, String b, String c ) {
this.a = a;
this.b = b;
this.c = c;
}
public String getA( ) {
return a;
}
public String getB( ) {
return b;
}
public String getC( ) {
return c;
}
}
class CustomAdapter extends XmlAdapter<String, CustomType>
{
#Override
public String marshal( CustomType v ) throws Exception {
return String.format("%s,%s,%s", v.getA(), v.getB(), v.getC());
}
#Override
/** Please don't use this in PROD :> */
public CustomType unmarshal( String v ) throws Exception {
String[] split = v.split(",");
return new CustomType(split[ 0 ], split[ 1 ], split[ 2 ]);
}
}
This should get you going, unless I completely misunderstood your question.
I have to deal with (a variation of) the following scenario. My model classes are:
class Car {
String brand;
Engine engine;
}
abstract class Engine {
}
class V12Engine extends Engine {
int horsePowers;
}
class V6Engine extends Engine {
String fuelType;
}
And I have to deserialize (no need for serialization support ATM) the following input:
<list>
<brand id="1">
Volvo
</brand>
<car>
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
<car>
<brand refId="1" />
<v6engine fuel="unleaded" />
</car>
</list>
What I've tried / issues:
I've tried using XStream, but it expects me to write tags such as:
<engine class="cars.V12Engine">
<horsePowers>300</horsePowers>
</engine>
etc. (I don't want an <engine>-tag, I want a <v6engine>-tag or a <v12engine>-tag.
Also, I need to be able to refer back to "predefined" brands based on identifiers, as shown with the brand-id above. (For instance by maintaining a Map<Integer, String> predefinedBrands during the deserialization). I don't know if XStream is well suited for such scenario.
I realize that this could be done "manually" with a push or pull parser (such as SAX or StAX) or a DOM-library. I would however prefer to have some more automation. Ideally, I should be able to add classes (such as new Engines) and start using them in the XML right away. (XStream is by no means a requirement, the most elegant solutions wins the bounty.)
JAXB (javax.xml.bind) can do everything you're after, though some bits are easier than others. For the sake of simplicity I'm going to assume that all your XML files have a namespace - it's trickier if they don't but can be worked around using the StAX APIs.
<list xmlns="http://example.com/cars">
<brand id="1">
Volvo
</brand>
<car>
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
<car>
<brand refId="1" />
<v6engine fuel="unleaded" />
</car>
</list>
and assume a corresponding package-info.java of
#XmlSchema(namespace = "http://example.com/cars",
elementFormDefault = XmlNsForm.QUALIFIED)
package cars;
import javax.xml.bind.annotation.*;
Engine type by element name
This is simple, using #XmlElementRef:
package cars;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Car {
String brand;
#XmlElementRef
Engine engine;
}
#XmlRootElement
abstract class Engine {
}
#XmlRootElement(name = "v12engine")
#XmlAccessorType(XmlAccessType.FIELD)
class V12Engine extends Engine {
#XmlAttribute
int horsePowers;
}
#XmlRootElement(name = "v6engine")
#XmlAccessorType(XmlAccessType.FIELD)
class V6Engine extends Engine {
// override the default attribute name, which would be fuelType
#XmlAttribute(name = "fuel")
String fuelType;
}
The various types of Engine are all annotated #XmlRootElement and marked with appropriate element names. At unmarshalling time the element name found in the XML is used to decide which of the Engine subclasses to use. So given XML of
<car xmlns="http://example.com/cars">
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
and unmarshalling code
JAXBContext ctx = JAXBContext.newInstance(Car.class, V6Engine.class, V12Engine.class);
Unmarshaller um = ctx.createUnmarshaller();
Car c = (Car)um.unmarshal(new File("file.xml"));
assert "BMW".equals(c.brand);
assert c.engine instanceof V12Engine;
assert ((V12Engine)c.engine).horsePowers == 300;
To add a new type of Engine simply create the new subclass, annotate it with #XmlRootElement as appropriate, and add this new class to the list passed to JAXBContext.newInstance().
Cross-references for brands
JAXB has a cross-referencing mechanism based on #XmlID and #XmlIDREF but these require that the ID attribute be a valid XML ID, i.e. an XML name, and in particular not entirely consisting of digits. But it's not too difficult to keep track of the cross references yourself, as long as you don't require "forward" references (i.e. a <car> that refers to a <brand> that has not yet been "declared").
The first step is to define a JAXB class to represent the <brand>
package cars;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Brand {
#XmlValue // i.e. the simple content of the <brand> element
String name;
// optional id and refId attributes (optional because they're
// Integer rather than int)
#XmlAttribute
Integer id;
#XmlAttribute
Integer refId;
}
Now we need a "type adapter" to convert between the Brand object and the String required by Car, and to maintain the id/ref mapping
package cars;
import javax.xml.bind.annotation.adapters.*;
import java.util.*;
public class BrandAdapter extends XmlAdapter<Brand, String> {
private Map<Integer, Brand> brandCache = new HashMap<Integer, Brand>();
public Brand marshal(String s) {
return null;
}
public String unmarshal(Brand b) {
if(b.id != null) {
// this is a <brand id="..."> - cache it
brandCache.put(b.id, b);
}
if(b.refId != null) {
// this is a <brand refId="..."> - pull it from the cache
b = brandCache.get(b.refId);
}
// and extract the name
return (b.name == null) ? null : b.name.trim();
}
}
We link the adapter to the brand field of Car using another annotation:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Car {
#XmlJavaTypeAdapter(BrandAdapter.class)
String brand;
#XmlElementRef
Engine engine;
}
The final part of the puzzle is to ensure that <brand> elements found at the top level get saved in the cache. Here is a complete example
package cars;
import javax.xml.bind.*;
import java.io.File;
import java.util.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Main {
public static void main(String[] argv) throws Exception {
List<Car> cars = new ArayList<Car>();
JAXBContext ctx = JAXBContext.newInstance(Car.class, V12Engine.class, V6Engine.class, Brand.class);
Unmarshaller um = ctx.createUnmarshaller();
// create an adapter, and register it with the unmarshaller
BrandAdapter ba = new BrandAdapter();
um.setAdapter(BrandAdapter.class, ba);
// create a StAX XMLStreamReader to read the XML file
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(new File("file.xml")));
xsr.nextTag(); // root <list> element
xsr.nextTag(); // first <brand> or <car> child
// read each <brand>/<car> in turn
while(xsr.getEventType() == XMLStreamConstants.START_ELEMENT) {
Object obj = um.unmarshal(xsr);
// unmarshal from an XMLStreamReader leaves the reader pointing at
// the event *after* the closing tag of the element we read. If there
// was a text node between the closing tag of this element and the opening
// tag of the next then we will need to skip it.
if(xsr.getEventType() != XMLStreamConstants.START_ELEMENT && xsr.getEventType() != XMLStreamConstants.END_ELEMENT) xsr.nextTag();
if(obj instanceof Brand) {
// top-level <brand> - hand it to the BrandAdapter so it can be
// cached if necessary
ba.unmarshal((Brand)obj);
}
if(obj instanceof Car) {
cars.add((Car)obj);
}
}
xsr.close();
// at this point, cars contains all the Car objects we found, with
// any <brand> refIds resolved.
}
}
Here's a solution with XStream, since you seem to already be familiar with it and since it's an incredibly flexible XML tool. It's done in Groovy because it's just so much nicer than Java. Porting to Java would be fairly trivial. Note that I opted to do a little post-processing of the result instead of trying to make XStream do all the work for me. Specifically, the "brand references" are handled after the fact. I could do it inside the marshalling, but I think this approach is cleaner and leaves your options more open for future modification. In addition, this approach allows "brand" elements to occur anywhere throughout the document, including after cars that refer to them--something I don't think you could accomplish if you were doing replacements on the fly.
Solution with annotations
import com.thoughtworks.xstream.XStream
import com.thoughtworks.xstream.annotations.*
import com.thoughtworks.xstream.converters.*
import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter
import com.thoughtworks.xstream.io.*
import com.thoughtworks.xstream.mapper.Mapper
// The classes as given, plus toString()'s for readable output and XStream
// annotations to support unmarshalling. Note that with XStream's flexibility,
// all of this is possible with no annotations, so no code modifications are
// actually required.
#XStreamAlias("car")
// A custom converter for handling the oddities of parsing a Car, defined
// below.
#XStreamConverter(CarConverter)
class Car {
String brand
Engine engine
String toString() { "Car{brand='$brand', engine=$engine}" }
}
abstract class Engine {
}
#XStreamAlias("v12engine")
class V12Engine extends Engine {
#XStreamAsAttribute int horsePowers
String toString() { "V12Engine{horsePowers=$horsePowers}" }
}
#XStreamAlias("v6engine")
class V6Engine extends Engine {
#XStreamAsAttribute #XStreamAlias("fuel") String fuelType
String toString() { "V6Engine{fuelType='$fuelType'}" }
}
// The given input:
String xml = """\
<list>
<brand id="1">
Volvo
</brand>
<car>
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
<car>
<brand refId="1" />
<v6engine fuel="unleaded" />
</car>
</list>"""
// The solution:
// A temporary Brand class to hold the relevant information needed for parsing
#XStreamAlias("brand")
// An out-of-the-box converter that uses a single field as the value of an
// element and makes everything else attributes: a perfect match for the given
// "brand" XML.
#XStreamConverter(value=ToAttributedValueConverter, strings="name")
class Brand {
Integer id
Integer refId
String name
String toString() { "Brand{id=$id, refId=$refId, name='$name'}" }
}
// Reads Car instances, figuring out the engine type and storing appropriate
// brand info along the way.
class CarConverter implements Converter {
Mapper mapper
// A Mapper can be injected auto-magically by XStream when converters are
// configured via annotation.
CarConverter(Mapper mapper) {
this.mapper = mapper
}
Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Car car = new Car()
reader.moveDown()
Brand brand = context.convertAnother(car, Brand)
reader.moveUp()
reader.moveDown()
// The mapper knows about registered aliases and can tell us which
// engine type it is.
Class engineClass = mapper.realClass(reader.getNodeName())
def engine = context.convertAnother(car, engineClass)
reader.moveUp()
// Set the brand name if available or a placeholder for later
// reference if not.
if (brand.name) {
car.brand = brand.name
} else {
car.brand = "#{$brand.refId}"
}
car.engine = engine
return car
}
boolean canConvert(Class type) { type == Car }
void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
throw new UnsupportedOperationException("Don't need this right now")
}
}
// Now exercise it:
def x = new XStream()
// As written, this line would have to be modified to add new engine types,
// but if this isn't desirable, classpath scanning or some other kind of
// auto-registration could be set up, but not through XStream that I know of.
x.processAnnotations([Car, Brand, V12Engine, V6Engine] as Class[])
// Parsing will create a List containing Brands and Cars
def brandsAndCars = x.fromXML(xml)
List<Brand> brands = brandsAndCars.findAll { it instanceof Brand }
// XStream doesn't trim whitespace as occurs in the sample XML. Maybe it can
// be made to?
brands.each { it.name = it.name.trim() }
Map<Integer, Brand> brandsById = brands.collectEntries{ [it.id, it] }
List<Car> cars = brandsAndCars.findAll{ it instanceof Car }
// Regex match brand references and replace them with brand names.
cars.each {
def brandReference = it.brand =~ /#\{(.*)\}/
if (brandReference) {
int brandId = brandReference[0][1].toInteger()
it.brand = brandsById.get(brandId).name
}
}
println "Brands:"
brands.each{ println " $it" }
println "Cars:"
cars.each{ println " $it" }
Output
Brands:
Brand{id=1, refId=null, name='Volvo'}
Cars:
Car{brand='BMW', engine=V12Engine{horsePowers=300}}
Car{brand='Volvo', engine=V6Engine{fuelType='unleaded'}}
Solution without annotations
P.S. Just for grins, here's the same thing with no annotations. It's all the same except that instead of annotating the classes, there are several additional lines under the new XStream() that do everything the annotations were doing before. The output is identical.
import com.thoughtworks.xstream.XStream
import com.thoughtworks.xstream.converters.*
import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter
import com.thoughtworks.xstream.io.*
import com.thoughtworks.xstream.mapper.Mapper
class Car {
String brand
Engine engine
String toString() { "Car{brand='$brand', engine=$engine}" }
}
abstract class Engine {
}
class V12Engine extends Engine {
int horsePowers
String toString() { "V12Engine{horsePowers=$horsePowers}" }
}
class V6Engine extends Engine {
String fuelType
String toString() { "V6Engine{fuelType='$fuelType'}" }
}
String xml = """\
<list>
<brand id="1">
Volvo
</brand>
<car>
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
<car>
<brand refId="1" />
<v6engine fuel="unleaded" />
</car>
</list>"""
class Brand {
Integer id
Integer refId
String name
String toString() { "Brand{id=$id, refId=$refId, name='$name'}" }
}
class CarConverter implements Converter {
Mapper mapper
CarConverter(Mapper mapper) {
this.mapper = mapper
}
Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Car car = new Car()
reader.moveDown()
Brand brand = context.convertAnother(car, Brand)
reader.moveUp()
reader.moveDown()
Class engineClass = mapper.realClass(reader.getNodeName())
def engine = context.convertAnother(car, engineClass)
reader.moveUp()
if (brand.name) {
car.brand = brand.name
} else {
car.brand = "#{$brand.refId}"
}
car.engine = engine
return car
}
boolean canConvert(Class type) { type == Car }
void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
throw new UnsupportedOperationException("Don't need this right now")
}
}
def x = new XStream()
x.alias('car', Car)
x.alias('brand', Brand)
x.alias('v6engine', V6Engine)
x.alias('v12engine', V12Engine)
x.registerConverter(new CarConverter(x.mapper))
x.registerConverter(new ToAttributedValueConverter(Brand, x.mapper, x.reflectionProvider, x.converterLookup, 'name'))
x.useAttributeFor(V12Engine, 'horsePowers')
x.aliasAttribute(V6Engine, 'fuelType', 'fuel')
x.useAttributeFor(V6Engine, 'fuelType')
def brandsAndCars = x.fromXML(xml)
List<Brand> brands = brandsAndCars.findAll { it instanceof Brand }
brands.each { it.name = it.name.trim() }
Map<Integer, Brand> brandsById = brands.collectEntries{ [it.id, it] }
List<Car> cars = brandsAndCars.findAll{ it instanceof Car }
cars.each {
def brandReference = it.brand =~ /#\{(.*)\}/
if (brandReference) {
int brandId = brandReference[0][1].toInteger()
it.brand = brandsById.get(brandId).name
}
}
println "Brands:"
brands.each{ println " $it" }
println "Cars:"
cars.each{ println " $it" }
P.P.S. If you have Gradle installed, you can drop this into a build.gradle and one of the above scripts into src/main/groovy/XStreamExample.groovy, and then just gradle run it to see the result:
apply plugin: 'groovy'
apply plugin: 'application'
mainClassName = 'XStreamExample'
dependencies {
groovy 'org.codehaus.groovy:groovy:2.0.5'
compile 'com.thoughtworks.xstream:xstream:1.4.3'
}
repositories {
mavenCentral()
}
You can try referencing here to get some ideas.
Personally, I would use a DOM Parser to get the contents of the XML file.
Example:
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class DOMExample {
public static void main(String[] args) throws Exception {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
File file = new File("filename.xml");
Document doc = builder.parse(file);
NodeList carList = doc.getElementsByTagName("car");
for (int i = 0; i < carList.getLength(); ++i) {
Element carElem = (Element)carList.item(i);
Element brandElem = (Element)carElem.getElementsByTagName("brand").item(0);
Element engineElem = (Element)carElem.getElementsByTagName("v12engine").item(0);
String brand= brandElem.getTextContent();
String engine= engineElem.getTextContent();
System.out.println(brand+ ", " + engine);
// TODO Do something with the desired information.
}
}
}
If you know the possible contents of the tag names, this would work pretty well. There are many ways to parse through an XML file. Hopefully you can come up with something that works for you. Good luck!
I need to make sure that no object attribute is null and add default value in case if it is null. Is there any easy way to do this, or do I have to do it manually by checking every attribute by its getters and setters?
You can use reflection to iterate over the object's field, and set them. You'd obviously need some sort of mapping between types or even field names and required default values but this can be done quite easily in a loop. For example:
for (Field f : obj.getClass().getFields()) {
f.setAccessible(true);
if (f.get(obj) == null) {
f.set(obj, getDefaultValueForType(f.getType()));
}
}
[Update]
With modern Java, you can use annotations to set the default values for fields on a per class basis. A complete implementation might look like this:
// DefaultString.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface DefaultString {
String value();
}
// DefaultInteger.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface DefaultInteger {
int value();
}
// DefaultPojo.java:
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class DefaultPojo {
public void setDefaults() {
for (Field f : getClass().getFields()) {
f.setAccessible(true);
try {
if (f.get(this) == null) {
f.set(this, getDefaultValueFromAnnotation(f.getAnnotations()));
}
} catch (IllegalAccessException e) { // shouldn't happen because I used setAccessible
}
}
}
private Object getDefaultValueFromAnnotation(Annotation[] annotations) {
for (Annotation a : annotations) {
if (a instanceof DefaultString)
return ((DefaultString)a).value();
if (a instanceof DefaultInteger)
return ((DefaultInteger)a).value();
}
return null;
}
}
// Test Pojo
public class TestPojo extends DefaultPojo {
#DefaultString("Hello world!")
public String stringValue;
#DefaultInteger(42);
public int integerValue;
}
Then default values for a TestPojo can be set just by running test.setDetaults()
You need to manually filter input to constructors and setters. Well... you could use reflection but I wouldn't advise it. Part of the job of constructors and setters is to validate input. That can include things like:
public void setPrice(double price) {
if (price < 0.0d) {
throw new IllegalArgumentException("price cannot be negative " + price);
}
this.price = price;
}
and
public void setName(String name) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
}
You could use wrapper functions for the actual check and throwing the exception.
Non-reflective solution for Java 8, without using a series of if's, would be to stream all fields and check for nullness:
return Stream.of(id, name).allMatch(Objects::isNull);
This remains quite easy to maintain while avoiding the reflection hammer.
This will return true for null attributes.
Maybe check Hibernate Validator 4.0, the Reference Implementation of the JSR 303: Bean Validation.
This is an example of an annotated class:
public class Address {
#NotNull
private String line1;
private String line2;
private String zip;
private String state;
#Length(max = 20)
#NotNull
private String country;
#Range(min = -2, max = 50, message = "Floor out of range")
public int floor;
...
}
For an introduction, see Getting started with JSR 303 (Bean Validation) – part 1 and part 2 or the "Getting started" section of the reference guide which is part of the Hibernate Validator distribution.
You can create a function that returns a boolean value and checks every attribute. You can call that function to do the job for you.
Alternatively, you can initialize the object with default values. That way there is no need for you to do any checking.
I don't have enough context to give you a correct answer, but I'll suggest you to make you code immutable as much as possible. Use public final fields. No more getters or setters : every field has to be defined by the constructor. Your code is shorter, more readable and prevents you from writing code with side effects.
It doesn't prevent you from passing null arguments to your constructor though... You can still check every argument as suggested by #cletus, but I'll suggest you to throw IllegalArgumentException instead of NullPointerException that doesn't give no new hint about what you've done.
Anyway, that's what I do as much as I can and it improved my code (readability, stability) to a great extend. Everyone in my team does so and we are very happy with that. We learned that when we try to write some erlang code where everything is immutable.
Hope this helps.
I tried this and it works without any issues to validate if the field is empty.
I have answered your question partially as I haven't personally tried to add default values to attributes
if(field.getText()!= null && !field.getText().isEmpty())
Hope it helps
This is not to check for null, instead this will be helpful in converting an existing object to an empty object(fresh object). I dont know whether this is relevant or not, but I had such a requirement.
#SuppressWarnings({ "unchecked" })
static void emptyObject(Object obj)
{
Class c1 = obj.getClass();
Field[] fields = c1.getDeclaredFields();
for(Field field : fields)
{
try
{
if(field.getType().getCanonicalName() == "boolean")
{
field.set(obj, false);
}
else if(field.getType().getCanonicalName() == "char")
{
field.set(obj, '\u0000');
}
else if((field.getType().isPrimitive()))
{
field.set(obj, 0);
}
else
{
field.set(obj, null);
}
}
catch(Exception ex)
{
}
}
}