472,805 Members | 1,478 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,805 software developers and data experts.

Code Added: Facade to read & WRITE using XPath

I refined my attempt a little further, and the following code does
seem to work, however it has 2 major problems:

1. Very limited support for XPath features
Basic paths are supported for elements, attributes, ".", and "..",
plus also the "[expr='value']" predicate format is supported -
however, only one predicate per path step is supported, and expr must
be a relative path.

2. Poor performance
My knowledge of the full API for XML/XPath in Java is limited - so if
anyone out there knows more about it maybe you can give me some tips
to make this code work faster and use less memory.

If these don't bother you, feel free to use the following code. Enjoy!

//---------- SNIP ----------//
import java.io.*;
import java.util.*;
import org.apache.xerces.parsers.*;
import org.apache.xml.serialize.*;
import org.apache.xpath.*;
import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.xml.sax.*;

/**
* A Facade that encapsulates the complexities of the XML DOM API,
* providing a simple get/set interface for persisting information in
* XML using XPath.
*
* @author Will Hains
* @version 1.19
*/
public class XmlFacade
{
//// XmlFacade setup ////

/**
* The parsed XML tree.
*/
protected final Document _doc;

/**
* Loads the specified XML as the new document tree,
* discarding the previous tree.
*/
protected XmlFacade(String xmlText)
{
try
{
// Parse the XML document
DOMParser parser = new DOMParser();
parser.parse(new InputSource(new StringReader(xmlText)));
_doc = parser.getDocument();
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error loading XML: \n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

//// Read operations ////

/**
* @return the value of the specified Node.
*/
protected String getValue(Node nd) throws Exception
{
if(nd == null) return null;

// Element
if(nd.getNodeType() == Node.ELEMENT_NODE)
{
String flattenedValue = "";
nd.normalize();
NodeIterator i = XPathAPI.selectNodeIterator
(
nd,
"descendant::text()"
);
for(Node t = i.nextNode(); t != null; t = i.nextNode())
{
flattenedValue += t.getNodeValue();
}
return flattenedValue;
}

// Non-element
else return nd.getNodeValue();
}

/**
* @return the first value found at the specified path,
* or null if the path was not found.
*/
public String get(String xpath)
{
try
{
return getValue(XPathAPI.selectSingleNode(_doc, xpath));
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error retrieving from XPath: " + xpath + "\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

/**
* @return a List of all values found at the specified path.
*/
public List getList(String xpath)
{
try
{
List list = new Vector();
NodeIterator i = XPathAPI.selectNodeIterator(_doc, xpath);
for(Node nd = i.nextNode(); nd != null; nd = i.nextNode())
{
list.add(getValue(nd));
}
return list;
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error retrieving from XPath: " + xpath + "\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

/**
* @return an ordered Map of keys to values rooted at the
* specified path, and defined by the specified relative key and
* value paths.
*/
public Map getMap(String xpath, String keyPath, String valuePath)
{
Map map = new LinkedHashMap();
Object[] keys = getList(xpath + "/" + keyPath).toArray();
Object[] values = getList(xpath + "/" + valuePath).toArray();
for(int i = 0, length = keys.length; i < length; i++)
{
String key = (String)keys[i];
if(key != null) map.put(key, values[i]);
}
return map;
}

//// Delete operations ////

/**
* Deletes the specified node.
*/
protected void delete(Node nd) throws Exception
{
if(nd != null)
{
if(nd.getNodeType() == Node.ELEMENT_NODE)
{
nd.getParentNode().removeChild(nd);
}
else
{
Attr a = (Attr)nd;
a.getOwnerElement().removeAttributeNode(a);
}
}
}

/**
* Deletes the value at the specified path.
*/
public void delete(String xpath)
{
try
{
delete(XPathAPI.selectSingleNode(_doc, xpath));
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error deleting from XPath: " + xpath + "\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

//// Write operations ////

/**
* Lazily creates nodes based on the specified path.
* The implementation of this method determines the extent that
* this class can simulate read-write XPath support.
*
* @return a new node inserted into the doc at the specified path,
* or the node that already existed there.
*/
protected Node getNode(String xpath)
{
try
{
// Find the closest existing ancestor
Node nd = null;
String validPath = xpath;
for
(
int endOfValidPath = xpath.length();
endOfValidPath > 0;
endOfValidPath = validPath.lastIndexOf('/')
)
{
validPath = validPath.substring(0, endOfValidPath);
nd = XPathAPI.selectSingleNode(_doc, validPath);
if(nd != null) break;
}

// Create the remainder of the path
for
(
StringTokenizer tokens = new StringTokenizer
(
xpath.substring(validPath.length()),
"/"
);
tokens.hasMoreTokens();
)
{
// References to self can be ignored
String ndName = tokens.nextToken();
if(ndName.equals(".")) continue;

// Find predicate (supports only single condition)
String predicate = null;
int predicatePos = ndName.indexOf('[');
StringTokenizer predTokens = null;
if(predicatePos > 0)
{
predicate = ndName.substring
(
predicatePos + 1,
ndName.indexOf(']')
);
predTokens = new StringTokenizer
(
predicate,
" \t\n\r\f=\'\""
);
ndName = ndName.substring(0, predicatePos);
}

// Attribute
Node newNode;
if(ndName.indexOf('@') == 0)
{
String attrName = ndName.substring(1);
newNode = _doc.createAttribute(attrName);
((Element)nd).setAttributeNode((Attr)newNode);
}

// Element
else
{
newNode = _doc.createElement(ndName);
nd.appendChild(newNode);
}

// Update path to existing ancestor
validPath += "/" + ndName;
nd = newNode;

// Process predicates
if(predTokens != null)
{
// Find the predicate name and value
String predNdName = "";
String predNdValue = "";
try
{
while(predNdName.length() < 1)
{
predNdName = predTokens.nextToken();
}
while(predNdValue.length() < 1)
{
predNdValue = predTokens.nextToken();
}
}
catch(NoSuchElementException e)
{
throw new IllegalArgumentException
(
"This implementation does not support " +
"the predicate format: " +
predicate
);
}

// Element
if(predNdName.indexOf('@') < 0)
{
Element predicateElement = _doc.createElement(predNdName);
Node predicateTextNode = _doc.createTextNode(predNdValue);
predicateElement.appendChild(predicateTextNode);
nd.appendChild(predicateElement);
}

// Attribute
else ((Element)nd).setAttribute
(
predNdName.substring(1),
predNdValue
);
}
}

return nd;
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error retrieving/creating from XPath: " + xpath + "\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

/**
* Sets the value of the specified node.
*/
protected void setValue(Node nd, String value) throws Exception
{
// Node is an element - replace its contents
if(nd.getNodeType() == Node.ELEMENT_NODE)
{
for(int i = 0; i < nd.getChildNodes().getLength(); i++)
{
nd.removeChild(nd.getChildNodes().item(i));
}
nd.appendChild(_doc.createTextNode(value));
}

// Node is a non-element - set its value directly
else nd.setNodeValue(value);
}

/**
* Sets the value at the specified path.
*/
public void set(String xpath, boolean value)
{
set(xpath, Boolean.toString(value));
}

/**
* Sets the value at the specified path.
*/
public void set(String xpath, int value)
{
set(xpath, Integer.toString(value));
}

/**
* Sets the value at the specified path.
*/
public void set(String xpath, java.lang.Object value)
{
if(value == null) delete(xpath);
else set(xpath, value.toString());
}

/**
* Sets the value at the specified path.
*/
public void set(String xpath, String value)
{
try
{
setValue(getNode(xpath), value);
fireXmlChanged();
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error writing to XPath: " + xpath + "\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

/**
* Sets the values matching the specified path.
*/
public void setList(String xpath, Collection c)
{
try
{
// Make sure there is at least one matching node
if(c.size() > 0) getNode(xpath);

// Find existing nodes matching XPath query
Node prevNode = null;
NodeIterator i = XPathAPI.selectNodeIterator(_doc, xpath);
for(Iterator j = c.iterator(); j.hasNext();)
{
// Get the next value to be set
Object v = j.next();
String value = v != null ? v.toString() : null;

// A node exists in the current list position - change it
Node nd = i.nextNode();
if(nd != null) setValue(nd, value);

// The list is longer than there are existing nodes
else
{
// Element
if(prevNode.getNodeType() == Node.ELEMENT_NODE)
{
Element el = _doc.createElement(prevNode.getNodeName());
nd = prevNode.getParentNode().appendChild(el);
}

// Attribute
else
{
// Copy the previous node's owner element / parent node
Element owner = ((Attr)prevNode).getOwnerElement();
Element copy = _doc.createElement(owner.getNodeName());
owner = (Element)owner.getParentNode().appendChild(copy);

// Add a copy of the previous node and set its value
nd = _doc.createAttribute(((Attr)prevNode).getName());
owner.setAttributeNode((Attr)nd);
}

// Set the value of the new node
setValue(nd, value);
}

// Keep the current node for copying later
prevNode = nd;
}

// Remove redundant nodes when existing list is longer
List redundantNodes = new Vector();
for(Node nd = i.nextNode(); nd != null; nd = i.nextNode())
{
redundantNodes.add(nd);
}
for(Iterator r = redundantNodes.iterator(); r.hasNext();)
{
delete((Node)r.next());
}

// Clean up parent nodes that contain no other information
String voidParentsPath =
xpath.substring(0, xpath.lastIndexOf('/')) +
"[not(@*|node())]";
NodeIterator k = XPathAPI.selectNodeIterator
(
_doc,
voidParentsPath
);
for(Node n = k.nextNode(); n != null; n = k.nextNode())
{
delete(n);
}
fireXmlChanged();
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error writing to XPath: " + xpath + "\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

/**
* Sets a key-value pair list matching the specified paths to the
* contents of the specified Map.
* If you want to maintain the order of the keys, use an ordered Map
* implementation, such as TreeMap or LinkedHashMap.
*
* @param xpath the absolute path to the element or elements
* that contain the key-value pairs.
* @param keyPath the relative path from the element(s) specified by
* xpath to the element/attribute containing each key.
* @param valuePath the relative path from the element(s) specified
* by xpath to the element/attribute containing each value.
*/
public void setMap
(
String xpath,
String keyPath,
String valuePath,
Map map
)
{
try
{
// Delete existing nodes
List redundantNodes = new Vector();
NodeIterator n = XPathAPI.selectNodeIterator(_doc, xpath);
for(Node nd = n.nextNode(); nd != null; nd = n.nextNode())
{
redundantNodes.add(nd);
}
for(Iterator r = redundantNodes.iterator(); r.hasNext();)
{
delete((Node)r.next());
}

// Set map values
if(valuePath == null || valuePath.equals("")) valuePath = ".";
for(Iterator i = map.keySet().iterator(); i.hasNext();)
{
String key = (String)i.next();
set
(
xpath + "[" + keyPath + " = '" + key + "']/" + valuePath,
map.get(key)
);
fireXmlChanged();
}
}
catch(Exception e)
{
throw new IllegalArgumentException
(
"Error writing to XPath: " + xpath +
"{ " + keyPath + " => " + valuePath + " }\n" +
e.getClass().getName() + ": " +
e.getMessage()
);
}
}

public String toString()
{
try
{
OutputFormat format = new OutputFormat(_doc);
format.setStandalone(false);
format.setIndent(4);
format.setLineWidth(0);
StringWriter stringOut = new StringWriter();
XMLSerializer serial = new XMLSerializer(stringOut, format);
serial.serialize(_doc.getDocumentElement());
return stringOut.toString();
}
catch(IOException e)
{
return null;
}
}

//// Events & Listeners ////

private final Set _listeners = new HashSet();

public void addXmlFacadeListener(XmlFacadeListener l)
{
_listeners.add(l);
}

public void removeXmlFacadeListener(XmlFacadeListener l)
{
_listeners.remove(l);
}

protected void fireXmlChanged()
{
for(Iterator i = _listeners.iterator(); i.hasNext();)
{
((XmlFacadeListener)i.next()).dataChanged();
}
}
}

/**
* Unit test class.
* (Just testing the set methods for now.)
*/
class TestXmlFacade
{
public static void main(String[] args)
{
XmlFacade xml = new XmlFacade("<XF/>");
xml.set("/XF/Test[@no='1']", true);
xml.set("/XF/Test[@no='2']", 14);
xml.set("/XF/Test[@no='3']", "Hello World");
System.out.println(xml);

List list = new Vector();
list.add("Daria");
list.add("Trent");
list.add("Quinn");
list.add("Helen");
list.add("Jake");
list.add("Jane");
list.add("Tiffany");
xml.setList("/XF/Test[@no='4']/Morgendorffer/@name", list);
list.remove("Helen");
xml.setList("/XF/Test[@no='4']/Morgendorffer/@name", list);
list.add("Sandi");
xml.setList("/XF/Test[@no='4']/Morgendorffer/@name", list);
list.add(3, "Stacey");
xml.setList("/XF/Test[@no='4']/Morgendorffer/@name", list);
System.out.println(xml);

Map map = new LinkedHashMap();
map.put("Daria","F");
map.put("Quinn","F");
map.put("Helen","F");
map.put("Jane","F");
map.put("Tiffany","F");
map.put("Trent","M");
map.put("Jake","M");
xml.setMap("/XF/Test[@no='5']/Morgendorffer", "@name", null, map);
map.remove("Tiffany");
xml.setMap("/XF/Test[@no='5']/Morgendorffer", "@name", null, map);
map.put("Stacey","F");
xml.setMap("/XF/Test[@no='5']/Morgendorffer", "@name", null, map);
System.out.println(xml);
}
}
//---------- SNIP ----------//
public interface XmlFacadeListener
{
public void dataChanged();
}
//---------- SNIP ----------//
Jul 17 '05 #1
1 6735
Whoops! Shouldn't have changed the subject. That post was intended to
be in the same thread as:

http://groups.google.co.jp/groups?q=...gle.com&rnum=4
Jul 17 '05 #2

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

242
by: James Cameron | last post by:
Hi I'm developing a program and the client is worried about future reuse of the code. Say 5, 10, 15 years down the road. This will be a major factor in selecting the development language. Any...
2
by: Dan G. | last post by:
Hi there, I'm trying out the following code in order to read in an XML document. Private Sub GetXML_Click() Dim objDOMDocument As MSXML.DOMDocument Dim root As IXMLDOMElement Set...
2
by: ree32 | last post by:
When I import an xml document in Visual studio and Genereate as schema from it, and create a dataset from it, it adds this line into to the root element of my xml file -...
19
by: David Thielen | last post by:
Hi; If there are no namespaces this works fine for me. But if the xml has namespaces, then I get either no node back or an exception. Here is the sample xml: <root xmlns="http://www.test.org"...
1
by: Stijn Goris | last post by:
Hi all, I have created a XML structure with XSD and want to write and read data from the elements in the XML structure. Ik have for example an element called server and want to read the server...
6
by: Paolo Pignatelli | last post by:
I have an aspx code behind page that goes something like this in the HTML view: <asp:HyperLink id=HyperLink1 runat="server" NavigateUrl='<%#"mailto:" &amp;...
7
by: Rich Grise | last post by:
OK, I don't know if this is Off-Topic for the group(s), because "QT" isn't "Pure C++", and Slackware is a distro, but those guys are sharp. :-) And I've crossposted to sci.electroncs.design because...
7
by: Bilal | last post by:
Hello all, I came across this problem while working out the bugs in my identity trasnformation stylesheets but sidestepped it for later to see if there is an easier/better solution. This is...
12
by: InvalidLastName | last post by:
We have been used XslTransform. .NET 1.1, for transform XML document, Dataset with xsl to HTML. Some of these html contents contain javascript and links. For example: // javascript if (a &gt; b)...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 2 August 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
by: erikbower65 | last post by:
Using CodiumAI's pr-agent is simple and powerful. Follow these steps: 1. Install CodiumAI CLI: Ensure Node.js is installed, then run 'npm install -g codiumai' in the terminal. 2. Connect to...
0
linyimin
by: linyimin | last post by:
Spring Startup Analyzer generates an interactive Spring application startup report that lets you understand what contributes to the application startup time and helps to optimize it. Support for...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Sept 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
14
DJRhino1175
by: DJRhino1175 | last post by:
When I run this code I get an error, its Run-time error# 424 Object required...This is my first attempt at doing something like this. I test the entire code and it worked until I added this - If...
0
by: Rina0 | last post by:
I am looking for a Python code to find the longest common subsequence of two strings. I found this blog post that describes the length of longest common subsequence problem and provides a solution in...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
0
by: lllomh | last post by:
How does React native implement an English player?
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.