This article
Transfer from IBM DeveloperWorks China website
Create classes from XML data
The second article of the data binding series is how to generate a Java language from the XML data restriction. This article shows how to generate classes and code through the complete code, and provide suggestions for how to customize your own versions. Haven't I seen the first article? The first one, "object, everywhere," explained how data bindings conversion to the XML and Java language objects. It compares data binding and other methods that process XML in the Java program, and introduces an XML configuration document example. The first part also introduces the use of XML Schema to constrain the data. Before going deep into the Java program and XML code, first quickly review the basis of the first part of this series. In the first part, we know that as long as a set of constraints can be identified, the document can be converted to a Java object. Those constraints provide interfaces for data. As shown in the Web Services Configuration Documentation example, the XML document should be an instance of an existing Java class and generate that class from the data constraint. Finally, it will see XML Schema indicating the sample XML document constraint. If you have questions about the details, please review the first article. To create the foundation, you can start to create a Java class from XML Schema. This class must accurately represent data constraints, and provide simple reading methods and write methods that Java applications will use. Before you start, let's take a list 1, check the XML Schema defined for the WebServiceConfiguration document. Listing 1. XML Schema representing the Web Container Configuration Document Data Interface
XML Version = "1.0"?>
Before generating code Before generating Java code, you must first determine the name of the core class. The schemaMapper in the org.enhydra.xml.binding package will be used, which is part of the ENHYDRA application server utility class. You can also place any necessary support classes in this package. In addition to the name name, you must also determine the Java API used to read and create XML. As discussed in the previous article, three main options are SAX, DOM and JDOM. Since SAX is only suitable for reading an XML document, it is not suitable for creating XML. Since the Java object is converted to XML in the packet phase, XML is required in this stage. This reduces the range of the selected range to DOM and JDOM. In the case of these two options, I choose to use the JDOM API in this example, only to display its functionality (not just because I am one of its computers!). Finally, you must point out how to provide XML Schema to the SchemApper class. Typically, it can be assumed that the generated class is done off (by a static main method). You can also use classes from runtime environments by causing the main method. After doing these decisions, you can start the framework of the class. The first thing to assemble the Schemamapper class frame is to set some basic memories for the code to be generated. Multiple interfaces and implementations must be generated from each execution map XML Schema. Java HashMap is just satisfied with the requirements. The key is the interface or realization name and the value in the mapping table, which is the actual code that will be output to the new Java program file. It is also necessary to store the properties of each pair interface / implementation (attribute is shared between these two classes). Here, I use HashMap again. Where the key is the name of the interface. However, since each interface may have multiple properties, this value is another HashMap with attributes and their types. Finally, the namespace of XML Schema must be stored because JDOM will use this namespace to access the structure in XML Schema. All of these specific information is sufficient to start the framework of the new class, the new class in Listing 2. Also note that two basic methods that need to be used in Listing 2: one of the methods requires the use of XML Schema's URL to perform generated (allowing it to access Schema, and running under local Schema), another method will class Output to the specified directory. Finally, a simple main method treats XML Schema as a variable and then executes it. Listing 2. Schemamapper class frame package org.enhydra.xml.binding;
import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Iterator; import java.util.List; // JDOM classes used for document representationimport org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org .jdom.NoSuchAttributeException; import org.jdom.NoSuchChildException; import org.jdom.input.SAXBuilder; / ***
* * Allocate storage and set . up defaults * p> * / public SchemaMapper () {interfaces = new HashMap (); implementations = new HashMap (); properties = new HashMap (); schemaNamespace = Namespace.getNamespace (SCHEMA_NAMESPACE_URI);} / *** * this is the "Entry Point" for generation of java classes from an Xml * Schema. It allows a schema to be support, VIA , * AND That Schema is buy for infut to generation. * p> ** @Param Schemaurl * This will write out the generated classes to the supplied stream. * p> ** @ param directory * this provides a static entry point for class generation from * xml schema. * P> ** @Param args In Listing 2, you can see that the generated process is called for each XML Schema, the MAIN method transmitted as an argument. First, the method will generate a class. Convert file names to URL and pass to GenerateClasses (URL Schemaurl). Then, the class is written to the current directory through the WriteClasses (File Dir) method (converted into Java File: New file (".")). Any other Java class can perform the same call at runtime, and generate classes. For example, a custom class loader may find that you need to pack, determine the interfaces and implementations that still generate, and use the SchemAMAPper class to perform this task. All of this is done at runtime. Because the generateClasses () method requires a URL, it is very simple to use this class on the network. For example, it can be used to request an open-available XML Schema generated from HTTP. Because of how to use classes, it is a common class; the program can use it locally and remotely. And this class can be regarded as part of a set of Java languages and XML utility classes, rather than special classes that must be used in a particular form. This reusability principle is particularly critical to XML because network access and communication on different systems are the basic premise of XML. After generating a class, you can add the subject. I have mentioned that the generation process has recursive properties. Remember this, you need to fill your generateClasses () method to start. You can read XML Schema using JDOM and then extract each complexType element from Schema. For each of these elements, as shown in Listing 3, the recursive process begins at the HandleComplexType () call (further discussion will be discussed later). Listing 3. The generateClasses () method public void generateClasses (URL schemaURL) throws IOException {/ *** Create builder to generate JDOM representation of XML Schema, * without validation and using Apache Xerces * / SAXBuilder builder = new SAXBuilder ();. try {Document schemaDoc = builder.build (schemaURL); // Handle complex typesList complexTypes = schemaDoc.getRootElement () getChildren ( "complexType", schemaNamespace);. for (Iterator i = complexTypes.iterator (); i.hasNext ();) {// Iterate and handleElement ComplexType = (ELEMENT) i.Next (); handleComplexType (complexType);}} catch (jdomexception e) {throw new oException (E.getMessage ());}} For the sake of simplicity, it will emphasize some key points, not the entire process of converting Schema to the Java class. You can view the full SchemAmApper class online, or you can download it. The generator must determine that each ComplexType element found in XML Schema is an explicit (with "type" attribute) or implicit (without "type" properties). If the type is explicit, the type will be the interface name, and the initial capital is capitalized. If the type is implicit, the interface name will be constructed according to the characteristic name. The code segment for processing this logic is displayed in Listing 4. (To learn more about data binding, see the sidebar, the term explanation.) Listing 4. Determine the interface name // determine if this is an expedition or implicit typeString type = null; // handle extension, if NeededString BaseType = null; try {// Assume that we are dealing with an explicit typetype = complexType.getAttribute ( "name") getValue ();.} catch (NoSuchAttributeException e) {/ ** It is safe with an implicit type to assume that THE PARENT * IS OF TYPE "Element", HAS No "Type" Attribute, And That We * Can Derive The Type as The value of the element's "name" * attribute with the word "type" appended to it. * / try { .. type = new StringBuffer () append (.. BindingUtils.initialCaps (complexType.getParent () getAttribute ( "name") getValue ())) append ( "type") toString ();.} catch (NoSuchAttributeException nsae) { //Houldn't happen in schema-valid documentsthrow new oew oException ("All Elements Must At Have A Name);}} Therefore, according to the naming convention in the code, an element with a serviceconfiguration type will generate a Java interface called ServiceConfiguration. Elements called Port but there is no explicit type will generate a Java interface called PortType. It uses the element name (port) to transfer the initials to uppercase (port), plus the words TYPE, get PortType. Similarly, all implementations use the interface name and then add a abbreviation IMPL. So, the final implementation class is ServiceConfigurationImpl and PortTypeImpl. With these naming conventions, you can easily determine which Java classes that map data constraints to the Java interface. If the application is set in the runtime, the class loader or other utility can quickly determine if the required class is installed. Class loaders or utilities As long as you find the generated class name from XML Schema, you can try to load them. Named logic is determined in advance, so it is very convenient to check. Once the name is determined, an interface and an implementation class can be generated (see Listing 5). 5. Generate Code Listing StringBuffer interfaceCode = new StringBuffer (); StringBuffer implementationCode = new StringBuffer ();. / ** Start writing out the interface and implementation class * definitions * / interfaceCode.append ( "public interface") .append (interfaceName ); // add in extension if appropriateif (BaseType! = Null) {interfaceCode.Append ("Extends") .append (basetype);} interfacecode.Append ("{/ {"; "{//10) ) .append (implementationName); // Add in extension if appropriateif (baseType = null) {implementationCode.append ( "extends") .append (baseType) .append ( "Impl");}! implementationCode.append ( "implements" ) .append ("{// n"); // add in proties and methods // close Up interface and ustementation classinterfacecode.append ("}"); ImplementationCode.Append ("}"); In fact, generating properties and methods are quite simple. Add the interface and the corresponding name of the corresponding implementation to the memory of the class, then the right broth, and their role is the end class. Like this pair of pairs, rather than separately generate classes, will simultaneously reflect the process on the interface and implementation. Check the source code (see Resources), you can get enough explanation. The bold annotation in Listing 5 represents multiple lines of code in the source list. The streamlined code here is to keep it simple. For each feature of XML Schema (represented by Schema Attribute), the read method and write method are added to the interface and implementation (implementation of the code logic). At the same time, variables will be added to the code of the class. The final result is a class that is generated in the first part of this series. Where they can be viewed or downloaded along with the rest of the code in this article (see Resources): ServiceConfiguration.java ServiceConfigurationImpl.java PortType.java PortTypeImpl.java DocumentType.java DocumentTypeImpl.java WebServiceConfiguration.java WebServiceConfigurationImpl.java two auxiliary The program class will also participate in class generation: bindingutils, turn the initials into capital. Although this method can be added to the generator class, but I plan to use this method in the package and dispenser class, so I will return it to this auxiliary program class. You can view BindingUtils online, or you can download it. DataMapping, SchemamApper class is used to convert data types. You can view the source code or download the source code online. Complete the package such as many other open source software, the data binding package shown here is a work in progress. Although it has already taken a scale, there is still a lot of space available to add more features and improvements. Therefore, based on this code, there are many ways to be derived in applications. This sample code can be reused to convert the data constraints of XML Schema into type secure JAVA interfaces and implementations. For example, so far, sample code has not been processed in XML Schema. For many XML developers, those data ranges are the real cause of Schema. Then consider the expansion XML Schema of the web service in Listing 6. Listing 6. Web service configuration with expansion constraints XML Version = "1.0"?> Complextype> public class PortTypeImpl implements PortType {private String protocol; private int number; private String protected; public void setNumber (int number) {if ((number> 0) && (number <= 32767)) {this.number = number;} else {throw IllegalArgumentException ( "Argument must be greater than 0and less than or equal to 32767");}} public int getNumber () {return number;} public void setProtocol (String protocol) {this.protocol = protocol;} public String getProtocol () {Return protocol;} public void setprotected (String protected) {this.protaced = protected;}} public string getprotected ()} SchemaMapper code> handles generation of Java interfaces and classes * from an XML schema, essentially allowing data contracts to be set up * for the binding of XML instance documents to Java objects. * p> ** @author Brett McLaughlin * / public class SchemaMapper {/ ** Storage for code for interfaces * / private Map interfaces ; / ** Storage for code for importations * / private map importations; / ** Properties That Accessor / Mutators Should Be CREATED for * / Protected Map Properties; / ** XML Schema N amespace * / private Namespace schemaNamespace; / ** XML Schema Namespace URI * / private static final String SCHEMA_NAMESPACE_URI = "http://www.w3.org/1999/xmlSchema"; / ***
URL code>
URL code> at Which XML Schema is located. * @throws
oException code> - when problems in generation occur. * / public void generateClasses (URL schemaURL) throws IOException {// Perform generation} / ***
File code> to write to (should be a directory) * @throws
IOException code> -.. when output errors occur * / public void writeClasses (File dir) throws IOException {/ / ***
string [] code> list of Files to parse. * / public static void main (string [] args) {schemaMapper mapper = new schemaMAPper (); try {for (int i = 0; i