Data Binding from XML to Java code II

Source: Internet
Author: User
Tags ibm developerworks
This article is posted on the IBM developerworks Chinese website.

Create a class from XML data

The second article in the Data Binding series is how to generate a Java language from XML data restrictions. This article uses the complete code to show how to generate classes and code, and provides suggestions on how to customize your own version. Have you read the first article? Article 1: "objects, ubiquitous objects" explains how data binding converts XML and Java language objects to each other. It compares data binding with other methods for processing XML in Java programs, and introduces an xml configuration document example. The first part also introduces the use of XML schema to constrain data.

Before going deep into Java programs and XML code, let's take a quick look at the basics of the first part of this series.

In the first part, we know that as long as a set of constraints of the document can be identified, the document can be converted to a Java object. These constraints provide interfaces for data. As shown in the Web service configuration document example, the XML document should be an instance of an existing Java class and generate that class from the data constraints. Finally, we can see the XML schema that represents the constraints of the sample XML document.

If you have any questions about the details, please review the first article.

  Build the Foundation

Now, you can create Java classes from XML schema. This class must accurately represent data constraints and provide the simple read and write methods that Java applications will use. Before starting, let's review listing 1 to view the XML schema defined for the webserviceconfiguration document.

Listing 1. XML schema of the Web container configuration document data interface

<? XML version = "1.0"?>

<Schema targetnamespace = "http://www.enhydra.org"
Xmlns = "http://www.w3.org/1999/xmlSchema"
Xmlns: enhydra = "http://www.enhydra.org"
>

<Complextype name = "serviceconfiguration">
<Attribute name = "name" type = "string"/>
<Attribute name = "version" type = "float"/>
</Complextype>

<Element name = "serviceconfiguration" type = "serviceconfiguration"/>

<Complextype name = "webserviceconfiguration"
Basetype = "serviceconfiguration"
Derivedby = "extension">
<Element name = "Port">
<Complextype>
<Attribute name = "protocol" type = "string"/>
<Attribute name = "Number" type = "integer"/>
<Attribute name = "protected" type = "string"/>
</Complextype>
</Element>

<Element name = "document">
<Complextype>
<Attribute name = "root" type = "string"/>
<Attribute name = "Index" type = "string"/>
<Attribute name = "error" type = "string"/>
</Complextype>
</Element>
</Complextype>

<Element name = "webserviceconfiguration" type = "webserviceconfiguration"/>

</Schema>

  Generate code

Before generating Java code, you must first determine the name of the core class. Schemamapper In the org. enhydra. xml. Binding package will be used, which is part of the set of enhydra Application Server utilities. You can also put any necessary support classes in this package.

In addition to the class name, you must also determine the Java API used to read and create XML. As discussed in the previous article, the three main options are sax, Dom, and JDOM. Since Sax is only applicable to reading XML documents, it is not suitable for creating XML files. To convert a Java object to an XML Representation in the packaging phase, you must create an XML file in this phase. This will narrow down the selection scope to Dom and JDOM. When both options are available, I chose to use the jdom api in this example only to display its functionality (not just because I am one of its co-authors !).

Finally, you must specify how to provide the XML schema to the schemamapper class. Generally, it can be assumed that the generation of the class is completed offline (through the static main method ). You can only use the main method to call non-static methods and use classes in the runtime environment. After making these decisions, you can start to sketch the class framework.

  Assemble the schemamapper class framework

The first thing to do is to set some basic memory for the code to be generated. You must be able to generate multiple interfaces and implementations from each XML schema that executes the ing. Java hashmap exactly meets the requirements. The key is the interface or implementation name and the value in the ing table. This value is the actual code to be output to the new Java program file. You also need to store the attributes of each pair of interfaces/implementations (the attributes are shared between the two types ). Here, I use hashmap again. The key is the interface name. However, since each interface may have multiple attributes, this value is another hashmap with attributes and their types. Finally, the namespace of the XML Schema must be stored because JDOM uses this namespace to access the structure in the XML schema. All of this information is sufficient to preliminarily outline the framework of the new class, which is included in Listing 2.

Note that you have added two basic methods in Listing 2: one method needs to be generated using the XML schema URL (allowing it to run in the network accessible schema and local schema), and the other method outputs the class to the specified directory. Finally, the simple main method regards XML schema as a variable, and then executes the generation.

Listing 2. Framework Package org. enhydra. xml. Binding of the schemamapper class;

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 Representation
Import 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;

/**
* <P>
* <Code> 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 implementations */
Private map implementations;

/** Properties that accessor/mutators shoshould be created */
Protected map properties;

/** XML Schema namespace */
Private namespace schemanamespace;

/** XML Schema namespace URI */
Private Static final string schema_namespace_uri =
"Http://www.w3.org/1999/xmlSchema ";

/**
* <P>
* Allocate storage and set up defaults.
* </P>
*/
Public schemamapper (){
Interfaces = new hashmap ();
Implementations = new hashmap ();
Properties = new hashmap ();
Schemanamespace = namespace. getnamespace (schema_namespace_uri );
}

/**
* <P>
* This is the "entry point" for generation of Java classes from an XML
* Schema. It allows a schema to be supplied, via <code> URL </code>,
* And that schema is used for input to generation.
* </P>
*
* @ Param schemaurl <code> URL </code> at which XML schema is located.
* @ Throws <code> ioexception </code>-when problems in generation occur.
*/
Public void generateclasses (URL schemaurl) throws ioexception {
// Perform generation
}

/**
* <P>
* This will write out the generated classes to the supplied stream.
* </P>
*
* @ Param directory <code> file </code> to write to (shocould be a directory ).
* @ Throws <code> ioexception </code>-when output errors occur.
*/
Public void writeclasses (File DIR) throws ioexception {
// Perform output to files
}

/**
* <P>
* This provides a static entry point for class generation from
* XML schemas.
* </P>
*
* @ Param ARGs <code> string [] </code> list of files to parse.
*/
Public static void main (string [] ARGs ){
Schemamapper mapper = new schemamapper ();
Try {
For (INT I = 0; I <args. length; I ++ ){
File file = new file (ARGs [I]);
Mapper. generateclasses (file. tourl ());
Mapper. writeclasses (new file ("."));
}
} Catch (filenotfoundexception e ){
System. Out. println ("cocould not locate XML Schema :");
E. printstacktrace ();
} Catch (ioexception e ){
System. Out. println ("Java class generation failed :");
E. printstacktrace ();
}
}
}

In Listing 2, we can see that the main method calls the generation process for each XML Schema passed as the independent variable. First, the method generates a class. Convert the file name to a URL and pass it to generateclasses (URL schemaurl ). Then, use the writeclasses (File DIR) method to write the class to the current directory (convert to Java file: New file (".")).

Any other Java class can make the same call at runtime and generate a class. For example, a custom class loader may find the interface and implementation to be generated, and use the schemamapper class to execute the 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, you can use it to request to publish available XML Schema generation classes from HTTP.

Because we make as few assumptions as possible about how to use a class, it is a common class. programs can use it locally and remotely. In addition, this class can be used as part of a set of Java language and XML Utility Classes, rather than dedicated classes that must be used in some special form. This reusability principle is especially critical to XML, because network access and communication on different systems are the basic premise of XML.

  Generate class

After building the framework of the class, you can add the class subject.

I have mentioned that the generation process is recursive. Remember this. You must fill in the generateclasses () method to start. You can use JDOM to read the XML schema and then extract each complextype element from the schema. For each of these elements, as shown in listing 3, the recursive process starts from the handlecomplextype () call (which will be further 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 types
List complextypes = schemadoc. getrootelement ()
. Getchildren ("complextype ",
Schemanamespace );
For (iterator I = complextypes. iterator (); I. hasnext ();){
// Iterate and handle
Element complextype = (element) I. Next ();
Handlecomplextype (complextype );
}

} Catch (jdomexception e ){
Throw new ioexception (E. getmessage ());
}
}

For the sake of simplicity, I will emphasize some important points, rather than elaborate on the entire process of converting schema into Java classes. You can view the complete schemamapper class online or download it.

The generator must determine whether each complextype element found in the XML schema is explicit (with the "type" attribute) or implicit (without the "type" attribute ). If the type is explicit, the type becomes the interface name, and the first letter is capitalized. If the type is implicit, the interface name is constructed based on the feature name. Listing 4 shows the code segment that processes this logic. (For more data binding definitions, see the sidebar and glossary .)

Listing 4. Determine the interface name // determine if this is an explict or implicit type

String type = NULL;
// Handle extension, if needed
String basetype = NULL;

Try {
// Assume that we are dealing with an explicit type
Type = 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 ){
// Shouldn't happen in Schema-valid documents
Throw new ioexception ("all elements must at have a name .");
}
}

Therefore, according to the naming conventions in the code, elements of the serviceconfiguration type will generate a Java interface named serviceconfiguration. Elements with the name of port but no explicit type will generate a Java interface called porttype. It uses the element name (port), converts the first letter to the upper letter (port), and adds the word type to get the porttype.

Similarly, all implementation classes use the interface name and then add the abbreviation impl. Therefore, the final implementation classes are serviceconfigurationimpl and porttypeimpl.

Using these naming conventions, you can easily determine which Java classes will be obtained by ing data constraints to Java interfaces. If an application is set to enter the class at runtime, the class loader or other utilities can quickly determine whether the required class has been loaded. The class loader or utility just needs to find the generated class names from the XML schema and then try to load them. The naming logic is pre-determined, so it is very convenient to check.

Once the name is determined, the framework of interfaces and implementation classes can be generated (see listing 5 ).

Listing 5. Generating code

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 appropriate
If (basetype! = NULL ){
Interfacecode. append ("extends ")
. Append (basetype );
}

Interfacecode. append ("{/N ");

Implementationcode. append ("public class ")
. Append (implementationname );

// Add in extension if appropriate
If (basetype! = NULL ){
Implementationcode. append ("extends ")
. Append (basetype)
. Append ("impl ");
}

Implementationcode. append ("implements ")
. Append (interfacename)
. Append ("{/N ");

// Add in properties and Methods

// Close up interface and implementation classes
Interfacecode. append ("}");
Implementationcode. append ("}");

In fact, generating attributes and methods is quite simple. Add the interface and the corresponding implementation name to the class memory, and then the right curly braces. They are used to end the class. Generation of classes in pairs like this, instead of generating classes separately, will make it easy to reflect the process in both interfaces and implementations. Check the source code (see references) to get enough explanation.

The bold comment in listing 5 indicates multiple lines of code in the source list. Here the code is simplified to keep it concise. For each feature of the XML schema being created (represented by the schema attribute), the Read and Write methods are added to the interface and implementation (and the Code for implementing the execution method logic ). At the same time, variables will be added to the implementation class code.

The final result is the class generated in the first part of this series. You can view them here or download them with the rest of the code in this article (see references ):

Serviceconfiguration. Java

Serviceconfigurationimpl. Java

Porttype. Java

Porttypeimpl. Java

Documenttype. Java

Documenttypeimpl. Java

Webserviceconfiguration. Java

Webserviceconfigurationimpl. Java

There are two helper classes that will also participate in class generation:

Bindingutils, which converts the first letter to uppercase. Although this method can be added to the generator class, I plan to use this method later when packaging and resolving the package class, so I will add it to this helper class. You can view bindingutils online or download it.

Datamapping and schemamapper classes are used to convert data types. You can view the source code online or download the source code.


  Complete Package

For example, many other open-source software, the data binding package displayed here is an ongoing task. Although it has begun to take shape, there is still much space to add more features and make improvements. Therefore, based on this code, many methods can be derived from applications.

You can re-use the sample code to convert the data constraints of XML schema into type-safe Java interfaces and implementations. For example, so far, the sample code has not processed the range that may be specified in the XML schema. For many XML developers, the data scope is the real reason for using schema. Then, consider expanding the XML schema of the web service in Listing 6.

Listing 6. Web service configuration with expansion Constraints

<? XML version = "1.0"?>

<Schema targetnamespace = "http://www.enhydra.org"
Xmlns = "http://www.w3.org/1999/xmlSchema"
Xmlns: enhydra = "http://www.enhydra.org"
>

<Complextype name = "serviceconfiguration">
<Attribute name = "name" type = "string"/>
<Attribute name = "version" type = "float"/>
</Complextype>

<Element name = "serviceconfiguration" type = "serviceconfiguration"/>

<Complextype name = "webserviceconfiguration"
Basetype = "serviceconfiguration"
Derivedby = "extension">
<Element name = "Port">
<Complextype>
<Attribute name = "protocol" type = "string"/>
<Attribute name = "Number">
<Simpletype base = "integer">
<Minexclusive value = "0"/>
<Maxcompute sive value = "32767"/>
</Simpletype>
</Attribute>
<Attribute name = "protected" type = "string"/>
</Complextype>
</Element>

<Element name = "document">
<Complextype>
<Attribute name = "root" type = "string"/>
<Attribute name = "Index" type = "string"/>
<Attribute name = "error" type = "string"/>
</Complextype>
</Element>
</Complextype>

<Element name = "webserviceconfiguration" type = "webserviceconfiguration"/>

</Schema>

Listing 6 illustrates the type of the number attribute and specifies the valid range of values (1 to 32,767) in the rows highlighted in red ). The current version of schemamapper ignores these additional declarations. When creating Java interfaces and implementing classes from the schema, there is no need to process the minxxx and maxxxx keywords in the XML schema, but they can add a considerable amount of additional verification.

See the sample code in listing 7. The code is generated in the implementation class to ensure that only values in the specified range of schema can be used as variables.

Listing 7. Code generated with range check

Public class porttypeimpl implements porttype {
Private string protocol;
Private int number;
Private string protected;

Public void setnumber (INT number ){
If (number> 0) & <= 32767 )){
This. Number = number;
} Else {
Throw illegalargumentexception ("argument must be greater than 0
And 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. Protected = protected;
}

Public String getprotected (){
Return protected;
}
}

If an invalid value is provided for the class, the generated code block in listing 7 throws a runtime exception, which ensures type security and range security.

You can easily add enhancements similar to those in Listing 6 and listing 7 to the basic code I provide, because all the code in this article is open source. You may also want to join the enhydra architecture workgroup email sending list, where you maintain and discuss future versions and revisions of the Code. You can add this list from the enhydra web site, which is listed in the references in this article.

  Summary

So far, we have learned what data binding is. You already know the reason for using data binding, especially the configuration information. You have learned how to create an XML schema and configure the XML instance documentation for the Web Container service, and we have discussed in detail the org. enhydra. xml. Binding. schemamapper class. With this class, you can create a Java interface and (the interface) implementation, which will manage the Java instances created from the XML document. You also know how to map constraints from XML schema to Java.

Now you can enter the next part. In the next section, we will begin to convert the XML document into a Java object, where the Java object is an instance of the generated class. The next article describes how to complete this process and its reverse process, as well as the org. enhydra. xml. Binding. unmarshaller and org. enhydra. xml. Binding. extends aller classes. These two classes move the XML format data of the text on the disk to the Java representation in the memory, and then move it back.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.