Java dynamic proxy example code: SAX XML handler
June 03, 2011 09:41:03 Last update: June 03, 2011 09:41:03
Dynamic proxy can be used to eliminate the need to stub out unused interface methods. This is an example for a simple SAX content handler for XML parsing.
- The
org.xml.sax.ContentHandlerinterface requires 11 methods be implemented but we only need three:import java.io.IOException; import java.util.Arrays; import org.xml.sax.helpers.XMLReaderFactory; import org.xml.sax.XMLReader; import org.xml.sax.ContentHandler; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; public class SaxParserDemo { public static void main(String[] args) throws SAXException, IOException { XMLReader p = XMLReaderFactory.createXMLReader(); p.setContentHandler(new DemoContentHandler()); p.parse("demo.xml"); } } class DemoContentHandler implements ContentHandler { public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { System.out.printf("<%s>", qName); } public void characters(char[] text, int start, int length) throws SAXException { System.out.print(Arrays.copyOfRange(text, start, start + length)); } public void endElement(String namespaceURI, String localName, String qName) throws SAXException { System.out.printf("</%s>", qName); } // do nothing methods public void setDocumentLocator(Locator locator) {} public void startDocument() throws SAXException {} public void endDocument() throws SAXException {} public void startPrefixMapping(String prefix, String uri) throws SAXException {} public void endPrefixMapping(String prefix) throws SAXException {} public void skippedEntity(String name) throws SAXException {} public void ignorableWhitespace(char[] text, int start, int length) throws SAXException {} public void processingInstruction(String target, String data) throws SAXException {} }
- With a dynamic proxy, we don't need the empty blocks for the unused methods:
import java.io.IOException; import java.util.Arrays; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import org.xml.sax.helpers.XMLReaderFactory; import org.xml.sax.XMLReader; import org.xml.sax.ContentHandler; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; public class SaxParserDemo2 { public static void main(String[] args) throws SAXException, IOException { XMLReader p = XMLReaderFactory.createXMLReader(); p.setContentHandler((ContentHandler) Proxy.newProxyInstance( SaxParserDemo2.class.getClassLoader(), new Class[] { ContentHandler.class }, new SaxDemoInvocationHandler() )); p.parse("demo.xml"); } } class SaxDemoInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) { String methodName = method.getName(); if (methodName.equals("startElement")) { System.out.printf("<%s>", args[2]); } else if (methodName.equals("characters")) { char[] text = (char[]) args[0]; int start = ((Integer) args[1]).intValue(); int length = ((Integer) args[2]).intValue(); System.out.print(Arrays.copyOfRange(text, start, start + length)); } else if (methodName.equals("endElement")) { System.out.printf("</%s>", args[2]); } return null; } }
- Equivalently (with anonymous inner class):
import java.io.IOException; import java.util.Arrays; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import org.xml.sax.helpers.XMLReaderFactory; import org.xml.sax.XMLReader; import org.xml.sax.ContentHandler; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; public class SaxParserDemo2 { public static void main(String[] args) throws SAXException, IOException { XMLReader p = XMLReaderFactory.createXMLReader(); p.setContentHandler((ContentHandler) Proxy.newProxyInstance( SaxParserDemo2.class.getClassLoader(), new Class[] { ContentHandler.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) { String methodName = method.getName(); if (methodName.equals("startElement")) { System.out.printf("<%s>", args[2]); } else if (methodName.equals("characters")) { char[] text = (char[]) args[0]; int start = ((Integer) args[1]).intValue(); int length = ((Integer) args[2]).intValue(); System.out.print(Arrays.copyOfRange(text, start, start + length)); } else if (methodName.equals("endElement")) { System.out.printf("</%s>", args[2]); } return null; } } )); p.parse("demo.xml"); } }
-
demo.xml:<breakfast-menu> <food> <name>Belgian Waffles</name> <price>$5.95</price> <description> two of our famous Belgian Waffles with plenty of real maple syrup </description> <calories>650</calories> </food> <food> <name>Strawberry Belgian Waffles</name> <price>$7.95</price> <description> light Belgian waffles covered with strawberries and whipped cream </description> <calories>900</calories> </food> <food> <name>Berry-Berry Belgian Waffles</name> <price>$8.95</price> <description> light Belgian waffles covered with an assortment of fresh berries and whipped cream </description> <calories>900</calories> </food> </breakfast-menu>