Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
The Extensible Markup Language (XML) standard from the Worldwide Web Consortium (W3C) defines a language-neutral approach for electronic data exchange. Its rigorous set of rules enables the structure inherent in data to be easily encoded and unambiguously interpreted using human-readable text documents.
View objects support the ability to write these XML documents based on their queried data. View objects also support the ability to read XML documents in order to apply changes to data including inserts, updates, and deletes. When you've introduced view links, this XML capability supports reading and writing multi-level nested information for master/detail hierarchies of any complexity. While the XML produced and consumed by view objects follows a canonical format, you can combine the view object's XML features with XML Stylesheet Language Transformations (XSLT) to easily convert between this canonical XML format and any format you need to work with.
Note: The examples in this section refer to theReadingAndWritingXML project in the AdvancedViewObjectExamples workspace. See the note at the beginning of this chapter for download instructions. |
To produce XML from a view object, use the writeXML()
method. If offers two ways to control the XML produced:
For precise control over the XML produced, you can specify a view object attribute map indicating which attributes should appear, including which view link accessor attributes should be accessed for nested, detail information:
Node writeXML(long options, HashMap voAttrMap)
To producing XML that includes all attributes, you can simply specify a depth-level that indicates how many levels of view link accessor attributes should be traversed to produce the result:
Node writeXML(int depthCount, long options)
The options
parameter is a integer flag field that can be set to one of the following bit flags:
XMLInterface.XML_OPT_ALL_ROWS
Includes all rows in the view object's row set in the XML.
XMLInterface.XML_OPT_LIMIT_RANGE
Includes only the rows in the current range in the XML.
Using the logical OR operation, you can combine either of the above flags with the XMLInterface.XML_OPT_ASSOC_CONSISTENT
flag when you want to include new, unposted rows in the current transaction in the XML output.
Both versions of the writeXML()
method accept an optional third argument which is an XSLT stylesheet that, if supplied, is used to transform the XML output before returning it.
When you produce XML using writeXML()
, the view object begins by creating a wrapping XML element whose default name matches the name of the view object definition. For example, for a Users
view object in the devguide.advanced.xml.queries
package, the XML produces will be wrapped in an outermost Users tag.
Then, it converts the attribute data for the appropriate rows into XML elements. By default, each row's data is wrapped in an row element whose name is the name of the view object with the Row
suffix. For example, each row of data from a view object named Users
is wrapped in an UsersRow element. The elements representing the attribute data for each row appear as nested children inside this row element.
If any of the attributes is a view link accessor attribute, and if the parameters passed to writeXML()
enable it, the view object will include the data for the detail rowset returned by the view link accessor. This nested data is wrapped by an element whose name is determined by the name of the view link accessor attribute. The return value of the writeXML()
method is an object that implements the standard W3C Node
interface, representing the root element of the generated XML.
Note: ThewriteXML() method uses view link accessor attributes to programmatically access detail collections. It does not require adding view link instances in the data model. |
For example, to produce an XML element for all rows of a Users
view object instance, and following view link accessors as many levels deep as exists, Example 27-19 shows the code required.
Example 27-19 Generating XML for All Rows of a View Object to All View Link Levels
ViewObject vo = am.findViewObject("Users"); printXML(vo.writeXML(-1,XMLInterface.XML_OPT_ALL_ROWS));
The Users
view object is linked to a ServiceRequests
view object showing the service requests created by that user. In turn, the ServiceRequests
view object is linked to a ServiceHistories
view object providing details on the notes entered for the service request by customers and technicians. Running the code in Example 27-19 produces the XML shown in Example 27-20, reflecting the nested structure defined by the view links.
Example 27-20 XML from a Users View Object with Two Levels of View Linked Details
<Users> : <User> <UserId>316</UserId> <UserRole>user</UserRole> <EmailAddress>sbaida</EmailAddress> <FirstName>Shelli</FirstName> <LastName>Baida</LastName> <StreetAddress>4715 Sprecher Rd</StreetAddress> <City>Madison</City> <StateProvince>Wisconsin</StateProvince> <PostalCode>53704</PostalCode> <CountryId>US</CountryId> <UserRequests> <ServiceRequestsRow> <SvrId>101</SvrId> <Status>Closed</Status> <RequestDate>2006-04-16 13:32:54.0</RequestDate> <ProblemDescription>Agitator does not work</ProblemDescription> <ProdId>101</ProdId> <CreatedBy>316</CreatedBy> <AssignedTo>304</AssignedTo> <AssignedDate>2006-04-23 13:32:54.0</AssignedDate> <ServiceHistories> <ServiceHistoriesRow> <SvrId>101</SvrId> <LineNo>1</LineNo> <SvhDate>2006-04-23 13:32:54.0</SvhDate> <Notes>Asked customer to ensure the lid was closed</Notes> <SvhType>Technician</SvhType> <CreatedBy>304</CreatedBy> </ServiceHistoriesRow> <ServiceHistoriesRow> <SvrId>101</SvrId> <LineNo>2</LineNo> <SvhDate>2006-04-24 13:32:54.0</SvhDate> <Notes>Problem is fixed</Notes> <SvhType>Customer</SvhType> <CreatedBy>316</CreatedBy> </ServiceHistoriesRow> </ServiceHistories> </ServiceRequestsRow> </UserRequests> </User> : </Users>
{para}?>
You can change the default XML element names used in the view object's canonical XML format by setting custom properties:
Set the custom attribute-level property named XML_ELEMENT
to a value SomeOtherName
to change the XML element name used for that attribute to SomeOtherName
For example, the Email
attribute in the Users
view object defines this property to change the XML element you see in Example 27-20 to be EmailAddress instead of Email.
Set the custom view object-level property named XML_ROW_ELEMENT
to a value SomeOtherRowName
to change the XML element name used for that attribute to SomeOtherRowName
For example, the Users
view object defines this property to change the XML element name for the rows you see in Example 27-20 to be User instead of UsersRow.
To change the name of the element names that wrapper nested row set data from view link attribute accessors, you need to use the View Link Properties panel of the View Link Editor to change the name of the view link accessor attribute.
By default, if a view row attribute is null
, then its corresponding element is omitted from the generated XML. You can set the custom attribute-level property named XML_EXPLICIT_NULL
to any value (e.g. "true
" or "yes
") to cause an element to be included for the attribute if its value is null. For example, if an attribute named AssignedDate
has this property set, then a row containing a null
assigned date will contain a corresponding AssignedDate null="true"/ element. If you want this behavior for all attributes of a view object, you can define the XML_EXPLICIT_NULL
custom property at the view object level as a shortcut for defining it on each attribute.
Two of the most common things you might want to do with the XML Node
object returned from writeXML()
are:
Printing the node to its serialized text representation — to send across the network or save in a file, for example
Searching the generated XML using W3C XPath expressions
Unfortunately, the standard W3C Document Object Model (DOM) API does not include methods for doing either of these useful operations. But there is hope. Since ADF Business Components uses the Oracle XML parser's implementation of the DOM, you can cast the Node
return value from writeXML()
to the Oracle specific classes XMLNode
or XMLElement
(in the oracle.xml.parser.v2
package) to access additional useful functionality like:
Printing the XML element to its serialized text format using the print()
method
Searching the XML element in memory with XPath expressions using the selectNodes()
method
Finding the value of an XPath expression related to the XML element using the valueOf()
method.
Example 27-21 shows the printXML()
method in the TestClientWriteXML
. It casts the Node
parameter to an XMLNode and calls the print()
method to dump the XML to the console.
When you need fine control over which attributes appear in the generated XML, use the version of the writeXML()
method that accepts a HashMap
. Example 27-22 shows the interesting lines from a TestClientWriteXML
class that use this technique. After creating the HashMap
, you put String[]
-valued entries into it containing the names of the attributes you want to include in the XML, keyed by the fully-qualified name of the view definition those attributes belong to. The example includes the UserId
, Email
, StateProvince
, and UserRequests
attributes from the Users
view object, and the SvrId
, Status
, AssignedDate
, and ProblemDescription
attributes from the ServiceRequests
view object.
Note: For upward compatibility reasons with earlier versions of ADF Business Components theHashMap expected by the writeXML() method is the one in the com.sun.java.util.collections package. |
While processing the view rows for a given view object instance:
If an entry exists in the attribute map with a key matching the fully-qualified view definition name for that view object, then only the attributes named in the corresponding String
array are included in the XML.
Furthermore, if the string array includes the name of a view link accessor attribute, then the nested contents of its detail row set are included in the XML. If a view link accessor attribute name does not appear in the string array, then the contents of its detail row set are not included.
If no such entry exists in the map, then all attributes for that row are included in the XML.
Example 27-22 Using a View Definition Attribute Map for Fine Control Over Generated XML
HashMap viewDefMap = new HashMap(); viewDefMap.put("devguide.advanced.xml.queries.Users", new String[]{"UserId", "Email", "StateProvince", "UserRequests" /* View link accessor attribute */ }); viewDefMap.put("devguide.advanced.xml.queries.ServiceRequests", new String[]{"SvrId","Status","AssignedDate","ProblemDescription"}); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap));
Running the example produces the XML shown in Example 27-23, including only the exact attributes and view link accessors indicated by the supplied attribute map.
Example 27-23 XML from a Users View Object Produced Using an Attribute Map
<Users> <User> <UserId>300</UserId> <EmailAddress>sking</EmailAddress> <StateProvince>Washington</StateProvince> <UserRequests> <ServiceRequestsRow> <SvrId>200</SvrId> <Status>Open</Status> <AssignedDate null="true"/> <ProblemDescription>x</ProblemDescription> </ServiceRequestsRow> </UserRequests> </User> <User> <UserId>301</UserId> <EmailAddress>nkochhar</EmailAddress> <StateProvince>Maryland</StateProvince> </User> : </Users>
If your view objects are related through a view link that you have configured to be bi-directional, then you must use the writeXML()
approach that uses the attribute map. If you were to use the writeXML()
approach in the presence of bi-directional view links and were to supply a maximum depth of -1
to include all levels of view links that exist, the writeXML()
method will go into an infinite loop as it follows the bi-directional view links back and forth, generating deeply nested XML containing duplicate data until it runs out of memory. Use writeXML()
with an attribute map instead in this situation. Only by using this approach can you control which view link accessors are included in the XML and which are not to avoid infinite recursion while generating the XML.
When the canonical XML format produced by writeXML()
does not meet your needs, you can supply an XSLT stylesheet as an optional argument. It will produce the XML as it would normally, but then transform that result using the supplied stylesheet before returning the final XML to the caller.
Consider the XSLT stylesheet shown in Example 27-24. It is a simple transformation with a single template that matches the root element of the generated XML from Example 27-23 to create a new CustomerEmailAddresses element in the result. The template uses the xsl:for-each instruction to process all User element children of Users that contain more than one ServiceRequestsRow child element inside a nested UserRequests element. For each User element that qualifies, it creates a Customer element in the result whose Contact
attribute is populated from the value of the EmailAddress child element of the User.
Example 27-24 XSLT Stylesheet to Transform Generated XML Into Another Format
<?xml version="1.0" encoding="windows-1252" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <CustomerEmailAddresses> <xsl:for-each select="/Users/User[count(UserRequests/ServiceRequestsRow) > 1]"> <xsl:sort select="EmailAddress"/> <Customer Contact="{EmailAddress}"/> </xsl:for-each> </CustomerEmailAddresses> </xsl:template> </xsl:stylesheet>
Example 27-25 shows the interesting lines from a TestClientWriteXML
class that put this XSLT stylesheet into action when calling writeXML()
.
Example 27-25 Passing an XSLT Stylesheet to writeXML() to Transform the Resulting XML
// In TestClientWriteXML.java XSLStylesheet xsl = getXSLStylesheet(); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap,xsl));
Running the code in Example 27-25 produces the transformed XML shown here:
<CustomerEmailAddresses> <Customer Contact="dfaviet"/> <Customer Contact="jchen"/> <Customer Contact="ngreenbe"/> </CustomerEmailAddresses>
The getXSLStylesheet()
helper method shown in Example 27-26 is also interesting to study since it illustrates how to read a resource like an XSLT stylesheet from the classpath at runtime. The code expects the Example.xsl
stylesheet to be in the same directory as the TestClientWriteXML
class. By referencing the Class
object for the TestClientWriteXML
class using the .class
operator, the code uses the getResource()
method to get a URL
to the resource. Then, it passes the URL to the newXSLStylesheet()
method of the XSLProcessor
class to create a new XSLStylesheet
object to return. That object represents the compiled version of the XSLT stylesheet read in from the *.xsl
file.
Example 27-26 Reading an XSLT Stylesheet as a Resource from the Classpath
private static XSLStylesheet getXSLStylesheet() throws XMLParseException, SAXException,IOException,XSLException { String xslurl = "Example.xsl"; URL xslURL = TestClientWriteXML.class.getResource(xslurl); XSLProcessor xslProc = new XSLProcessor(); return xslProc.newXSLStylesheet(xslURL); }
Note: When working with resources like XSLT stylesheets that you want to be included in the output directory along with your compiled Java classes and XML metadata, you can use the Compiler page of the Project Properties dialog to update the Copy File Types to Output Directory field to include.xsl in the semicolon-separated list. |
In addition to calling writeXML()
on a view object, you can call the same method with the same parameters and options on any Row
as well. If the Row
object on which you call writeXML()
is a entity row, you can bitwise-OR the additional XMLInterface.XML_OPT_CHANGES_ONLY
flag if you only want the changed entity attributes to appear in the XML.
To have a view object consume an XML document to process inserts, updates, and deletes, use the readXML()
method:
void readXML(Element elem, int depthcount)
The canonical format expected by readXML()
is the same as what would be produced by a call to the writeXML()
method on the same view object. If the XML document to process does not correspond to this canonical format, you can supply an XSLT stylesheet as an optional third argument to readXML()
to transform the incoming XML document into the canonical format before it is read for processing.
When a view object consumes an XML document in canonical format, it processes the document to recognize row elements, their attribute element children, and any nested elements representing view link accessor attributes. It processes the document recursively to a maximum level indicated by the depthcount
parameter. Passing -1
for the depthcount
to request that it process all levels of the XML document.
For each row element it recognizes, the readXML()
method does the following:
Identifies the related view object to process the row.
Reads the children attribute elements to get the values of the primary key attributes for the row.
Performs a findByKey()
using the primary key attributes to detect whether the row already exists or not.
If the row exists:
If the row element contains the marker attribute bc4j-action="remove"
, then the existing row is deleted.
Otherwise, the row's attributes are updated using the values in any attribute element children of the current row element in the XML
If the row does not exist, then a new row is created, inserted into the view object's rowset. Its attributes are populated using the values in any attribute element children of the current row element in the XML.
The same readXML()
method is also supported on any Row
object. The canonical XML format it expects is the same format produced by a call to writeXML()
on the same row. You can invoke readXML()
method on a row to:
Update its attribute values from XML
Remove the row, if the bc4j-action="remove"
marker attribute is present on the corresponding row element.
Insert, update, or delete any nested rows via view link accessors
Consider the XML document shown in Example 27-27. It is in the canonical format expected by a single row in the Technicians
view object. Nested inside the root TechniciansRow element, the City attribute represents the technician's city. The nested ExpertiseAreas element corresponds to the ExpertiseAreas
view link accessor attribute and contains three ExpertiseAreasRow elements. Each of these includes ProdId elements representing part of the two-attribute primary key of a ExpertiseAreas
row. The other primary key attribute, UserId
, is inferred from the enclosing parent TechniciansRow element.
Example 27-27 XML Document in Canonical Format to Insert, Update, and Delete Rows
<TechniciansRow> <!-- This will update Techncian's City attribute --> <City>Padova</City> <ExpertiseAreas> <!-- This will be an update since it does exist --> <ExpertiseAreasRow> <ProdId>100</ProdId> <ExpertiseLevel>Expert</ExpertiseLevel> </ExpertiseAreasRow> <!-- This will be an insert since it doesn't exist --> <ExpertiseAreasRow> <ProdId>110</ProdId> <ExpertiseLevel>Expert</ExpertiseLevel> </ExpertiseAreasRow> <!-- This will be deleted --> <ExpertiseAreasRow bc4j-action="remove"> <ProdId>112</ProdId> </ExpertiseAreasRow> </ExpertiseAreas> </TechniciansRow>
Example 27-28 shows the interesting lines of code from a TestClientReadXML
class that applies this XML datagram to a particular row in the Technicians
view object, and to the nested set of the technician's areas of expertise via the view link accessor attribute to the ExpertiseAreas
view object. TestClientReadXML
class performs the following basic steps:
Finds a target row by key (e.g. for technician "ahunold").
Shows the XML produced for the row before changes are applied.
Obtains the parsed XML document with changes to apply using a helper method.
Reads the XML document to apply changes to the row.
Shows the XML with the pending changes applied.
TestClientReadXML
class is using the XMLInterface.XML_OPT_ASSOC_CONSISTENT
flag described in Section 27.7.1, "How to Produce XML for Queried Data" to ensure that new, unposted rows are included in the XML.
Example 27-28 Applying Changes to and Existing Row with readXML()
ViewObject vo = am.findViewObject("Technicians"); Key k = new Key(new Object[] { 303 }); // 1. Find a target row by key (e.g. for technician "ahunold") Row ahunold = vo.findByKey(k, 1)[0]; // 2. Show the XML produced for the row before changes are applied printXML(ahunold.writeXML(-1, XMLInterface.XML_OPT_ALL_ROWS)); // 3. Obtain parsed XML document with changes to apply using helper method Element xmlToRead = getInsertUpdateDeleteXMLGram(); printXML(xmlToRead); // 4. Read the XML document to apply changes to the row ahunold.readXML(getInsertUpdateDeleteXMLGram(), -1); // 5. Show the XML with the pending changes applied printXML(ahunold.writeXML(-1, XMLInterface.XML_OPT_ALL_ROWS | XMLInterface.XML_OPT_ASSOC_CONSISTENT));
Running the code in Example 27-28 initially displays the "before" version of Alexander Hunold's information. Notice that:
The City
attribute has the value "Southlake"
The expertise area for product 100
has a level of "Qualified"
There is an expertise row for product 112
, and
There is no expertise area row related to product 110
.
<TechniciansRow> <UserId>303</UserId> <UserRole>technician</UserRole> <Email>ahunold</Email> : <City>Southlake</City> : <ExpertiseAreas> <ExpertiseAreasRow> <ProdId>100</ProdId> <UserId>303</UserId> <ExpertiseLevel>Qualified</ExpertiseLevel> </ExpertiseAreasRow> : <ExpertiseAreasRow> <ProdId>112</ProdId> <UserId>303</UserId> <ExpertiseLevel>Expert</ExpertiseLevel> </ExpertiseAreasRow> : </ExpertiseAreas> </TechniciansRow>
After applying the changes from the XML document using readXML()
to the row and printing its XML again using writeXML() you see that:
The City
is now "Padova"
The expertise area for product 100
has a level of "Expert"
The expertise row for product 112
is removed, and
A new expertise area row for product 110
got created.
<TechniciansRow> <UserId>303</UserId> <UserRole>technician</UserRole> <Email>ahunold</Email> : <City>Padova</City> : <ExpertiseAreas> <ExpertiseAreasRow> <ProdId>110</ProdId> <UserId>303</UserId> <ExpertiseLevel>Expert</ExpertiseLevel> </ExpertiseAreasRow> <ExpertiseAreasRow> <ProdId>100</ProdId> <UserId>303</UserId> <ExpertiseLevel>Expert</ExpertiseLevel> </ExpertiseAreasRow> </TechniciansRow>
Note: The example illustrated usingreadXML() to apply changes to a single row. If the XML document contained a wrapping Technicians row, including the primary key attribute in each of its one or more nested TechniciansRow elements, then that document could be processed using the readXML() method on the Technicians view object for handling operations for multiple Technician rows. |