简体   繁体   中英

Java SAX parser & multiple ContentHandlers

I've written a Java SAX parser, on Android, using XmlReader and DefaultHandler .

Its growing size has become unwieldy so what I want to do is delegate responsibility for content beneath certain elements to 'child' handlers.

Suppose I have the XML:

<fruit>
   <apples>
      <pips/>
   </apples>
   <oranges>
      <seeds/>
   </oranges>
</fruit>

I intended to have the original handler implementation handover to separate respective handlers for apples, oranges etc, and have those hand back when done.

Therefore, I do something a bit like this:

@Override
public void startElement( ... ) ...
{
   //...

   if ( localName.equals("oranges"))
   {
       ContentHandler orangeHandler = new OrangeHandler( xmlReader, this );
       xmlReader.setContentHandler( orangeHandler );
   }

   super.startElement( uri, localName, qName, attributes );
}

and then restore the main handler in a similar way.

What I expect to see : further calls to startElement(), characters(), endElement() etc go to the new handler.

What I actually see : that, but continued calls to those methods on the main handler as well.

Javadoc says:

public void setContentHandler(ContentHandler handler)

Allow an application to register a content event handler.

If the application does not register a content handler, all content events reported by the SAX parser will be silently ignored.

Applications may register a new or different handler in the middle of a parse, and the SAX parser must begin using the new handler immediately.

I can get around the reporting behaviour by keeping my own record of whether I'm delegating or not, and ignoring those surplus calls, but it makes me wonder if I'm doing something stupid that will bite me later.

Does anyone have experience of this?

I think it's perhaps easier to maintain your original SAX parser that issues your callbacks, and substitute in separate handlers underneath that simply proxy the SAX callback methods with different implementations, but don't perform the actual XML reading , as you're currently doing.

eg (in pseudocode)

class SaxHandler {
   SaxProxy proxy = new SaxProxyImpl();
   public void startElement(e) {
      proxy.startElement(e);
   }
}

and swap your proxy to a different implementation as required. I suspect you'll need a stack of proxies, and pop your stack upon the appropriate endElement() callback.

As a resolution, I've found this to be a bug in my own code - compared to the code in the question, the more complicated reality is that I have multiple levels of delegation, plus reprocessing of the delegation point, ie

   if ( localName.equals("oranges"))
   {
       ContentHandler orangeHandler = new OrangeHandler( xmlReader, this );
       xmlReader.setContentHandler( orangeHandler );
       orangeHandler.startElement( ..., localName, ...);
   }

In doing so, I had inappropriate ordering of the content handler and startElement calls, which - when delegating down for a second time - conspired to set the wrong handler.

I don't think it reduces all my complexities, but @Brian's choice of design would have simplified the delegation of parsing flow itself, so I'm accepting that as the answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM