Skip navigation.
Arch2Arch Tab BEA.com

Native XML Scripting in BEA WebLogic Workshop

by John Schneider
09/12/2002

It’s no secret - XML’s popularity has skyrocketed, making it a significant part of our daily jobs. How much time have you spent in the last year pouring over XML-related specifications; learning new ways to create, navigate, and manipulate XML data; and trying to decipher and debug that XSLT or DOM code you (or that new guy) wrote a few months ago?

Have you ever stopped to wonder whether all these new XML concepts, processing models, and languages are really necessary? After all, you’ve been creating, navigating, and manipulating similarly complex data structures using objects for years. Isn’t there some way to flatten the XML learning curve and simplify XML processing by leveraging your existing programming skills and knowledge?

Take heart. Several companies have been collaborating to create a technology we call native XML scripting to do just that. BEA is the first to include native XML scripting in their products, but you will be seeing a lot more of it. In fact, ECMA plans to include it in future versions of the popular ECMAScript language (a.k.a. JavaScript).

This article will review some of the challenges associated with existing XML technologies, introduce you to native XML scripting, and describe a powerful, but little-known way to use native XML scripting in BEA WebLogic Workshop.

XSLT

Let’s start by taking a look at an example problem designed to show the simplicity of XSLT (taken from the XSLT tutorial at http://www.ibiblio.org/xml/books/bible2/chapters/ch17.html#d1e531). Given the following XML document,


 <PERIODIC_TABLE>
   <ATOM STATE="GAS">
     <NAME>Hydrogen</NAME>
     <SYMBOL>H</SYMBOL>
     <ATOMIC_NUMBER>1</ATOMIC_NUMBER>
     <ATOMIC_WEIGHT>1.00794</ATOMIC_WEIGHT>
     <BOILING_POINT UNITS="Kelvin">20.28</BOILING_POINT>
     <MELTING_POINT UNITS="Kelvin">13.81</MELTING_POINT>
     <DENSITY UNITS="grams/cubic centimeter">
       <!-- At 300K, 1 atm -->
       0.0000899
     </DENSITY>
   </ATOM>
   <ATOM STATE="GAS">
     <NAME>Helium</NAME>
     <SYMBOL>He</SYMBOL>
     <ATOMIC_NUMBER>2</ATOMIC_NUMBER>
     <ATOMIC_WEIGHT>4.0026</ATOMIC_WEIGHT>
     <BOILING_POINT UNITS="Kelvin">4.216</BOILING_POINT>
     <MELTING_POINT UNITS="Kelvin">0.95</MELTING_POINT>
     <DENSITY UNITS="grams/cubic centimeter"><!-- At 300K -->
       0.0001785
     </DENSITY>
   </ATOM>

we’d like create an HTML view of the document in the following form:


      <html>
        <P>
          Hydrogen
          H
          1
          1.00794
          20.28
          13.81
          0.0000899
        </P>
        <P>
          Helium
          He
          2
          4.0026
          4.216
          0.95
          0.0001785
        </P>
      </html>

We can perform this task using the following XSLT code:


 <?xml version="1.0"?>
 <xsl:stylesheet version="1.0"
           xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="PERIODIC_TABLE">
     <html>
       <xsl:apply-templates/>
     </html>
   </xsl:template>
   <xsl:template match="ATOM">
     <P>
       <xsl:apply-templates/>
     </P>
   </xsl:template>
 </xsl:stylesheet>

To be fair, we’ve selected one of the simplest XSLT examples possible. Anyone who has used XSLT for real-world projects will testify that most XSLT code is significantly more complex than this example. Nonetheless, even with this simple example, it is obvious that XSLT is unlike familiar programming languages (Java, Visual Basic, C, etc.). The expression language and syntax are completely different. The programming model is declarative instead of procedural. The processing model is by default recursive instead of sequential. In addition, there are several new and unfamiliar concepts (templates, nodes, priority rules, etc.).

Consequently, the learning curve for XSLT is quite high, and deciphering it can be a challenge for newcomers. In fact, creating, deciphering, and modifying complex XSLT examples can test the mettle of even the best application developers. To exacerbate this problem, familiar design, development, debugging, profiling, and testing tools used to aid the creation of robust code cannot be used with XSLT.

The DOM

Application developers may feel more at home with the DOM. It allows programmers to use a familiar programming language to create, navigate, and manipulate XML via a tree-based API. For example, the problem above can be solved with the following DOM code fragment.


 public Document toHTML(Document srcDoc) throws Exception {
     Document destDoc = srcDoc.getImplementation().createDocument("","HTML",null);
     Node root = destDoc.getFirstChild();

     Node node = srcDoc.getFirstChild().getFirstChild();

     while (node != null) {
       if (node.getNodeName().equals("ATOM")) {
         Node pNode = destDoc.createElement("P");
         root.appendChild(pNode);

         Node tempNode = node.getFirstChild();
         while (tempNode != null) {
           if (tempNode.getNodeType() == Node.ELEMENT_NODE) {
             Node n = tempNode.getFirstChild();
             while (n != null) {
               if (n.getNodeType() == Node.TEXT_NODE) {
                 Node tValue = destDoc.createTextNode(n.getNodeValue() + "\n");
                 pNode.appendChild(tValue);
               }

               n = n.getNextSibling();
             }
           }

           tempNode = tempNode.getNextSibling();
         }
       }

       node = node.getNextSibling();
     }

   return destDoc;
 }

This code is more familiar; however, the logic of the program is almost completely overwhelmed and obscured by tree navigation logic and DOM API calls. Consequently, creating, reading, and maintaining the code is tedious, time consuming and error prone. In addition, the DOM requires the programmer to learn and adopt a tree-based object model and a relatively extensive API for performing operations on this object model.

Introducing Native XML Scripting

While designing native XML scripting, our objective was to drastically simplify writing code to create, navigate, and manipulate XML by eliminating some of the challenges described above. Above all, we wanted a solution that was familiar and intuitive to developers, minimizing the need for specialized knowledge and flattening the XML learning curve. The result is code that is easy to read, write, and maintain without a huge stack of reference manuals by your side.

For example, the following native XML script can address the problem introduced above:


  function toHTML(srcDoc) {  
     var destDoc = <html/>;   // Start with an empty HTML document
     for (var atom in srcDoc..ATOM) {   // Loop through each ATOM tag in the source
     XML
        var ptext = ""; {
        for(var item in atom.children())   // Loop through each child of this atom
           ptext += item + "\n";   // and concatentate the text values
   
        destDoc[destDoc.length] =   // Append paragraph containing text values to
         <P>{ptext}</P>;           HTML
    }
    return destDoc;
 }


Even though the code above uses several advanced features from native XML scripting, it is simpler, more compact, and more familiar to the average programmer than the equivalent XSLT or DOM code. The logic of the code is not obscured by tree navigation logic, and it can be easily debugged using conventional debugging tools.

The code above should look particularly familiar to anyone who knows ECMAScript (a.k.a. JavaScript). Native XML scripting is based on and extends the popular ECMAScript language available in Web browsers and many environments where XML is found. It extends ECMAScript by adding several key language features, including native XML data types and operators that make XML a first-class citizen. Let’s take a closer look at some of the features of native XML scripting for creating, navigating, filtering, iterating through, and modifying XML documents.

Creating XML

The first thing that may strike you as interesting in the example above occurs on line two. On the right-hand side of the assignment operator, we see the XML literal, <html/>. XML literals are used to create XML values, just like numeric and string literals are used to create numeric and string values. This eliminates the need to invoke an XML parser or use DOM methods to manually construct an XML value by inserting nodes into a tree structure.

Any legal XML value can be used as an XML literal, including those that span multiple lines. For example, the following code constructs an XML value representing a periodic table and assigns it to the variable x.


 var x = <PERIODIC_TABLE>
           <ATOM STATE="GAS">
             <NAME>Hydrogen</NAME>
             <SYMBOL>H</SYMBOL>
             <ATOMIC_NUMBER>1</ATOMIC_NUMBER>
             <ATOMIC_WEIGHT>1.00794</ATOMIC_WEIGHT>
             <BOILING_POINT UNITS="Kelvin">20.28</BOILING_POINT>
             <MELTING_POINT UNITS="Kelvin">13.81</MELTING_POINT>
             <DENSITY UNITS="grams/cubic centimeter">
               <!-- At 300K, 1 atm -->
               0.0000899
             </DENSITY>
           </ATOM>
         </PERIODIC_TABLE>;

Navigating XML

A very common task in XML processing is navigation. Native XML scripting addresses this problem by adopting the familiar dot operator ".", commonly used for accessing the fields, methods and properties of an object, for accessing the children of an XML value. In addition, it adds a new dot-dot operator ".." for accessing descendents (i.e., children, grandchildren, etc.) of an XML value. These operators are conceptually similar to the "/" and "//" operators found in XPath, but they use syntax designed to be more intuitive for developers familiar with navigating objects (and they don’t conflict with the use of "/" and "//" for numeric division and comments).


 var y = <employees>
           <employee>
             <name>Joe</name>
             <age>28</age>
             <hobbies>
           <favorite>
                  <name>Sailing</name>
         </favorite>
               <others>
                  <name>Reading</name>
                  <name>Running</name>
           </others>
             </hobbies>
             <department>
               <name>Engineering</name>
             </department>
     </employee>
         </employees>;

 var name = y.employees.employee.name;    // Evaluates to the value "Joe"
 var age = y.employees.employee.age;                  // Evaluates to the value "28"

As the example above demonstrates, accessing the value of a child XML element is just like accessing a field or property of an object. At first glance, it may seem like the same result could be achieved by mapping the XML onto a standard object; however, there are important differences between XML objects and the objects we’ve come to know and love. For example, XML objects allow us to preserve and control the ordering of child XML elements, whereas object properties and fields have no concept of order. As another example, XML objects can contain several elements with the same name, which is not allowed for fields or properties of standard objects.

Continuing our example above, the following code demonstrates the dot-dot (or descendent) operator:

  var names = y..name;   // Returns a list of all "names" in the document
  // I.e., ["Joe", "Sailing", "Reading", "Running",
     "Engineering"]
  var hobbyNames =   // Returns a list of all hobby names in the document
  y..hobbies..name;   // I.e., ["Sailing", "Reading", "Running"]


This feature makes it easier to find XML elements in a document without specifying the complete containment hierarchy from the document root. You may have noticed that Line 3 in our first native XML scripting example above used the dot-dot (or descendent) operator to find all the elements named "ATOM" in the source XML document.

Filtering XML

Native XML scripting also introduces a filtering operator for identifying elements with contents that match specific criteria. Given an expression of the form xmlList.( booleanTest ), the filtering operator applies the booleanTest to each item in the xmlList and returns a new list containing all the matching items. For example, take a look at the following XML Script:


 var y = <employees>
           <employee>
             <department id="200">Product Management</department>
             <name>Joe</name>
             <age>28</age>
           </employee>
           <employee>
             <department id="500">Engineering</department>
             <name>Bill</name>
             <age>34</age>
           </employee>
           <employee>
             <department id="500">Engineering</department>
             <name>Mary</name>
             <age>29</age>
           </employee>
           <employee>
             <department id="500">Engineering</department>
             <name>Ken</name>
             <age>24</age>
           </employee>
         </employees>;

 var over27inEng = y.employees.employee.(department.@id == 500 && age > 27);

The last line in this script finds all employees in department 500 who are older than 27. After executing this line, the variable over27inEng will contain a list referencing the second and third <employee> elements in the document containing information about Bill and Mary respectively. I.e.,


 over27inEng[0] 1 <employee>
                    <department id="500">Engineering</department>
                    <name>Bill</name>
                    <age>34</age>
                        </employee>

 over27inEng[1] 1 <employee>
                    <department id="500">Engineering</department>
                    <name>Mary</name>
                    <age>29</age>

You may have also noticed that the last line in the above script uses the notation "@id" to access the attribute named ID. This nomenclature is a standard part of native XML scripting borrowed from XPath.

Iterating Through XML Lists

When an operator returns a list of items, it is often useful to iterate through the list performing the same set of operations on each item. As you may have noticed in our first example, native XML scripting reuses familiar looping constructs for this purpose. For example, given an XML value pointing to the root element of a purchase order in the following format:


 <order>
   <customer>
     <name>I. Wannabuy</name>
     <address>
       <street>53 Party Lane</street>
       <city>Anywhere</city>
       <state>CA</state>
       <zip>12345</zip>
     </address>
   </customer>
   <item id="4365">
     <description>Large Purple Dinosaur, Generic</description>
     <quantity>35</quantity>
     <price>24.99</price>
   </item>
   <item id="9986">
     <description>Catapult</description>
     <quantity>1</quantity>
     <price>149.95</price>
   </item>
   <item id="5765">
     <description>300 foot measuring tape</description>
     <quantity>1</quantity>
     <price>9.95</price>
   </item>
 </order>

The following function calculates the total price by iterating through the items.


 function calcTotal(order) {
   var total = 0;
   for (var item in order.item)
     total += item.price * item.quantity;

   return total;
 }

Note that this code looks exactly like the script you would write to perform the same operation on a "order" object containing an "item" array. Consequently, this code is very easy for the average application developer to write, understand, and maintain even without any special XML training.

Modifying XML

Up to this point, you’ve seen how to create, navigate, filter, and iterate through XML documents. To modify an XML document all you have to do is specify the portion you want to modify on the left-hand side of an assignment statement just like you would with an object. As an illustration, consider the following examples:


 // change the price of the third item in the order
 order..item[2].price = "8.99";

 // add an id attribute of the second item in the order
 order..item[1].@id = 12345;

 // change the age of the employee named Ken
 y.employees.employee.(name == "Ken").age = "27";

 // replace the second employee with a new one named Fred
 y.employees.employee[1] = <employee>
  <department id="500">Engineering<department>
         <name>Fred</name>
         <age>36</age>
 </employee>;

You can also delete portions of an XML document using the delete operator. For example:


 // delete the third employee
 delete y.employees.employee[2];

Building Classes with Native XML Scripting

Now that we’ve whet your appetite, let’s take a look at one of the little-known ways to use native XML scripting in WebLogic Workshop. The WebLogic Workshop documentation talks a lot about using native XML scripting from within XML Maps. However, few people know that you can also build entire classes with native XML scripting and call them from your JWS file.

Creating classes with native XML scripting is amazingly simple. In fact, you may have already done it without even knowing! Every JavaScript for XML (JSX) file you create in WebLogic Workshop is automatically compiled into a standard Java class when it is first used. The classname is derived from the JSX filename and the package name is derived from the directory structure. For example, given the project Test with the following structure,

1


WebLogic Workshop automatically compiles the Employee.jsx file into a class called Employee in the util package. Therefore, we can import it into our JWS Web service using the following directive:


 // import our new JavaScript for XML file
 import util.Employee;

When compiling the JSX file, WebLogic Workshop automatically generates a constructor and exposes each function declared in the file as a member function of the class. For example, given the following Employee.jsx file,


 // Construct an empty employee element
 var xmlDoc = <employee><name/><age/></employee>;

 // Set the employee name
 function setName (name) {
        xmlDoc.employee.name = name;
 }

 // Set the employee age
 function setAge (age) {
        xmlDoc.employee.age = age;
 }

 // Add an arbitrary annotation to the end of the employee document
 function annotate(annotation, value) {
        xmlDoc[xmlDoc.length] = <{annotation}>{value}</{annotation}>;
 }

 // return a standard W3C DOM Node representation of the employee element
 function toNode () {
 return xmlDoc.employee.domNode();
 }

WebLogic Workshop will create a single constructor with no parameters containing all initialization code defined in the global scope (i.e., not inside a function body). In the example above, the constructor will contain the single line of code:


 // Construct an empty employee element
 var xmlDoc = <employee><name/><age/></employee>;

In addition, WebLogic Workshop will create public member functions named setName(), setAge(), annotate() and toNode() corresponding to each function defined in the JSX file. All parameters and return values for the generated member function are of type java.lang.Object. The resulting class may be used within a JWS file just like familiar Java classes. For example, the following JWS method exercises all of the functions defined in Employee.jsx above:


    /**
     * @jws:operation
     * @jws:protocol soap-style="document"
     */
    public Node testEmployee() throws Exception {
        Employee e = new Employee();
        e.setName("Bill");
        e.setAge(new Integer(20));

        e.annotate("hobby", "Sailing");
        e.annotate("hobby", "Golfing");

        return (Node)e.toNode();
    }

On invocation, this Web service operations returns the following XML document wrapped in a SOAP envelope:


 <employee>
        <name>Bill</name>
        <age>20</age>
        <hobby>Sailing</hobby>
        <hobby>Golfing</hobby>
 </employee>

You may have noticed our use of the curly braces "{ " }" in the annotate() function above. Curly braces can be used anywhere inside an XML literal to dynamically compute parts of it. Any legal JavaScript expression can be used inside the curly braces.

Note that unlike static Java objects, the structure of native XML scripting objects can be modified dynamically at runtime. This dynamic nature allows us to tap into more of the flexibility of XML without learning a totally new language or large set of APIs.

Conclusion

In this article, I’ve reviewed some of the familiar challenges inherent in current XML technologies, given you a taste of the power and simplicity of native XML scripting, and shown you how tap into it using WebLogic Workshop. We’ve seen the promise of native XML scripting to drastically simplify creating, navigating, and manipulating XML. We’ve also seen how it leverages existing developer knowledge by reusing familiar constructs and extending them to simplify common XML operations like searching and filtering.

That said, we’ve only scratched the surface. Native XML scripting also includes support for XML namespaces, Xpath, and a variety of built-in operators and methods to simplify common XML tasks. You can find a lot more information about these and other features in your WebLogic Workshop documentation under the headings Handling XML with ECMAScript Extensions and Functions for Manipulating XML. You can also find several additional examples of JSX files in the samples project provided with WebLogic Workshop. Give it a try and let us know what you think!

Copyright © 2002 SYS-CON Media, Inc.