Here we want to describe and give the example to parse the java objects to xml and xml to java objects using the castor api with the user-defined mapping.
What is Castor XML
Castor XML is an XML data binding framework. Unlike the two main XML APIs, DOM (Document Object Model) and SAX (Simple API for XML) which deal with the structure of an XML document, Castor enables you to deal with the data defined in an XML document through an object model which represents that data.
Castor XML can marshal almost any “bean-like” Java Object to and from XML. In most cases the marshalling framework uses a set of ClassDescriptors and FieldDescriptors to describe how an Object should be marshalled and unmarshalled from XML.
For those not familiar with the terms “marshal” and “unmarshal”, it’s simply the act of converting a stream (sequence of bytes) of data to and from an Object. The act of “marshalling” consists of converting an Object to a stream, and “unmarshalling” from a stream to an Object.
Example:
Following are the Java beans to parse
Company.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package com.castor.model; import java.util.ArrayList; import java.util.List; /** * @author satish * */ public class Company { private int id; private Product product; private List<Employee> employees = new ArrayList<Employee>(); /** * */ public Company() { super(); } /** * @param id * @param product * @param employees */ private Company(int id, Product product, List<Employee> employees) { super(); this.id = id; this.product = product; this.employees = employees; } /** * It creates company instance. * * @param id * @param product * @param employees * @return */ public static Company get(int id, Product product, List<Employee> employees) { Company company = new Company(id, product, employees); return company; } // Setters, Getters and toString } |
Product.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package com.castor.model; /** * @author satish * */ public class Product { private int id; private String name; /** * */ public Product() { super(); } /** * @param id * @param name */ private Product(int id, String name) { super(); this.id = id; this.name = name; } /** * It creates the Product instance * * @param id * @param name * @return */ public static Product get(int id, String name) { Product product = new Product(id, name); return product; } // Setters, Getters and toString } |
Employee.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package com.castor.model; /** * @author satish * */ public class Employee { private int id; private String name; private String designation; /** * */ public Employee() { super(); } /** * @param id * @param name * @param designation */ private Employee(int id, String name, String designation) { super(); this.id = id; this.name = name; this.designation = designation; } /** * It creates the Employee instance * * @param id * @param name * @param designation * @return */ public static Employee get(int id, String name, String designation) { Employee employee = new Employee(id, name, designation); return employee; } // Setters, Getters and toString } |
→ We should need to specify the default(zero-parameterized) constructor to every bean. Otherwise it will throws the following exception.
“org.exolab.castor.mapping.MappingException: The Java class com.castor.model.Company is not constructable — it does not contain a default public constructor.”
→ We should need to specify setters and getters for fields. Otherwise it will throw the following exception.
“org.exolab.castor.mapping.MappingException: The method getId/isId in class com.castor.model.Company accepting/returning object of type int was not found”.
Following is the service to create the Company instances:
CompanyService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.castor.services; import com.castor.model.Company; /** * @author satish */ public interface CompanyService { /** * It establishes the company. * @return */ Company establish(); } |
CompanyServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.castor.services; import java.util.ArrayList; import java.util.List; import com.castor.model.Company; import com.castor.model.Employee; import com.castor.model.Product; /** * @author satish * */ public class CompanyServiceImpl implements CompanyService { /* * (non-Javadoc) * * @see com.castor.services.CompanyService#establish() */ @Override public Company establish() { Product application = Product.get(1, "application"); Employee vijay = Employee.get(1, "Vijay", "TL"); Employee pavan = Employee.get(2, "Pavan", "TM"); Employee venkatesh = Employee.get(3, "Venkatesh", "TM"); Employee satish = Employee.get(4, "Satish", "TM"); Employee chandu = Employee.get(5, "Chandu", "TM"); List<Employee> employees = new ArrayList<Employee>(); employees.add(vijay); employees.add(pavan); employees.add(venkatesh); employees.add(satish); employees.add(chandu); Company waveLabs = Company.get(1, application, employees); return waveLabs; } } |
Following is the mapping file to configure the user defined mapping, that allows the (partial) definition of a customized mapping between Java classes (and their properties) and XML.
company_mapping.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="UTF-8"?> <mapping> <class name="com.castor.model.Company"> <map-to xml="company" ns-uri="http://www.company.com/some.xsd" ns-prefix="ns2" /> <field name="id" type="int"> <bind-xml name="id" node="attribute" /> </field> <field name="product" type="com.castor.model.Product"> <bind-xml name="product" /> </field> <field name="employees" type="com.castor.model.Employee" collection="arraylist"> <bind-xml name="employee" node="element" location="employees"/> </field> </class> <class name="com.castor.model.Product"> <field name="id" type="int"> <bind-xml name="id" /> </field> <field name="name" type="string"> <bind-xml name="name" /> </field> </class> <class name="com.castor.model.Employee"> <field name="id" type="int"> <bind-xml name="id" /> </field> <field name="name" type="string"> <bind-xml name="name" /> </field> <field name="designation" type="string"> <bind-xml name="designation" /> </field> </class> </mapping> |
→ Here <mapping> is the root tag for Castor mapping file.
→ <class> is the child for it and this is respect to the java bean.
→ <map-to> is the child for class and in which we can specify the “xml” attribute value by how we want to see the class tag name.
→ <field> is the child for <class> which is respect to the field in the java bean.
→ <bind-xml> is the child for <field>, in which we can provide the “name” attribute value with how we want to see the tag names in generated xml file.
Here we have used some of other attributes for elements, a brief description about those as follows.
ns-uri: To specify the namespace.
ns-prefix: To specify the prefix for namespace.
node: To specify how our field to be generate as attribute or as element.
type: To specify the datatype of the field.
collection: If the datatype is the collection of something we need to specify the collection type.
location: Which is used to specify an element in which we want have some of other related child elements. We can give the name for it. Generally this can be useful for collection of objects.
For more information on the Castor mapping see the Castor Reference.
All set, now we need to test. Here we are providing the test with normal java main method.
CompanyParsingTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
package com.castor.main; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.ValidationException; import com.castor.model.Company; import com.castor.services.CompanyService; import com.castor.services.CompanyServiceImpl; /** * @author satish * */ public class CompanyParsingTest { /** * @param args */ public static void main(String[] args) { /* * Creating mapping Object */ Mapping mapping = new Mapping(); try { /* * loading the mapping file explicitly to the mapping Object */ mapping.loadMapping("C:/Users/satish/workspace/castordemo/src/com/castor/resources/company_mapping.xml"); /* * creating the writer Object with file name where we want to write * the parsed xml content */ Writer writer = new FileWriter("F:/Satish/softwares/Castor/company.xml"); // Marshalling Object to XML code -- STARTED /* * creating the Marhaller Object to to marshall the given writer */ Marshaller marshaller = new Marshaller(writer); /* * setting the mapping object to marshaller Object */ marshaller.setMapping(mapping); /* * creating the instance for company service to create the company */ CompanyService companyService = new CompanyServiceImpl(); /* * marshalling the given Company Object */ marshaller.marshal(companyService.establish()); /* * closing the writer */ writer.close(); // Marshalling Object to XMl code -- ENDED // UnMarshalling XMl to Object code -- STARTED /* * creating the reader Object to read the content from given file. */ Reader reader = new FileReader("F:/Satish/softwares/Castor/company.xml"); /* * creating the UnMarshaller Object with the class name to * UnMarshall */ Unmarshaller unmarshaller = new Unmarshaller(Company.class); /* * UnMarhalling the given XMl file */ Company company = (Company) unmarshaller.unmarshal(reader); /* * closing the reader Object */ reader.close(); /* * Printing the UnMarshlled Object */ System.out.println("Converted Object from XML:" + company); // UnMarshalling XMl to Object code -- ENDED } catch (IOException | MappingException | MarshalException | ValidationException e) { e.printStackTrace(); } } } |
→ In this client program we have provided the code, to both marsharlling(java To XML) and UnMarshalling(XML to Java).
→ The Castor will generates the output file with the default XML declaration as
“<?xml version=”1.0″ encoding=”UTF-8″ ?>”
If we want to customize the XML declaration we need to manage in java code like as follows
writer.append(“<?xml version=\”1.0\” encoding=\”UTF-8\” standalone=\”yes\”?>”);
After appending our own XML declaration to writer, we need to suppress Castor not to generate the XML declaration.
marshaller.setSupressXMLDeclaration(true);
→ When we specify the namespace prefix to the <class>, it will generate the prefix to all the child <field> elements which are having the pre-defined data types (it will not generate to the user- defined data types).
→ If we want not to generate namespace prefix we should manage that in java code and we need to configure that in mapping file as like normal fields. Castor is not provided any mapping mechanism for this.
Thank You 🙂