Solved

Nested XMLReader Only Seeing Root

Posted on 2002-03-23
9
304 Views
Last Modified: 2008-02-26
I have a situation where I have a large number of XML files that refer to each other in a nested fashion. Each time a reference elemnt (ref.element) is encountered, I need to follow it's pointer attribute (refid) to the corresponding and include the contents of that new file with the current file contents. It's like a depth-first traversal of a tree.

I've coded a Walker class that extends XMLFilterImpl that recursively follows these links by creating a new instance of itself. It correctly fires all of the SAX events that I have coded for the class, but the other XMLReaders that I have chained to the "root instance" only get the events from the root file processing.

For my application, I'm attaching an XSLT processor to the reader and it is only processing results from the root file.

I've include a simplifed source to illustrate what I want to do. I'll post some of my code if anyone wants to see it.

Thanks,
GG

Four input files...
     <root id='0'>
          <out>Zero</out>
          <ref.element refid='1' />
          <ref.element refid='2' />
     </root>
     
     <element id='1'>
          <out>One</out>
          <ref.element refid='3' />
     </element>
     
     <element id='2'>
          <out>Two</out>
     </element>
     
     <element id='3'>
          <out>Three</out>
     </element>

Desired output...
     Zero
     One
     Three
     Two

Recieved output...
     Zero
0
Comment
Question by:GleasonGuy
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2
  • 2
  • +1
9 Comments
 
LVL 14

Expert Comment

by:avner
ID: 6891928
You can use two methods :
Must of the XSL parsers have specific methods (like the MSXMLs selectNodes(str String) method) to reference between node names and strings.

the Other method is to use the ID and IDREF of XML , which will force you to use DTD or a like.
http://www.xml.com/pub/a/2000/10/04/linking/index.html

or just search for  "ID/IDREF" on the net.
0
 
LVL 14

Expert Comment

by:avner
ID: 6891949
GleasonGuy,

Please Ignore My response , I miss read your comment.

-Avner.
0
 
LVL 23

Expert Comment

by:b1xml2
ID: 6892067
Please post your code including the chaining portion. Thanx
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 2

Author Comment

by:GleasonGuy
ID: 6892095
The following is a working simplified version of my actual code. I ran it against the four source file I described in my original post and it produces the same un-desired results. In my code I have a lot more println() to see my progress. I removed them for spoace here.

Thanks,
GG
---
import java.io.File;
import java.io.FileInputStream;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;

public class RefWalker extends XMLFilterImpl  {

  private static final String DEFAULT_SAX_PARSER = "org.apache.xerces.parsers.SAXParser";

  public RefWalker(XMLReader xmlReader) {
    super(xmlReader);
  }

  public void startElement(String uri, String localName, String qName, Attributes attributes)
    throws SAXException {
    try {
      if( localName.equals("ref.element") ) {

        String sFile = attributes.getValue( "refid" );

        XMLReader myReader = XMLReaderFactory.createXMLReader(DEFAULT_SAX_PARSER);

        // Setup another "walker" to process this ref
        RefWalker refFilter = new RefWalker(myReader);

        // Setup the input source to add an entity declaration onto the input file
        InputSource inputSource = new InputSource(
          new FileInputStream( new File(sFile + ".xml") ) );

        // Parse the new ref document
        refFilter.parse( inputSource );
      } else {
        super.startElement(uri, localName, qName, attributes);
      }
    }
    catch (Exception e) {
      throw new SAXException(e);
    }
  }

  public void endElement(String uri, String localName, String qName)
    throws SAXException {

    try {
      if( localName.equals("ref.element") ) {
        // Do Nothing
      } else {
        super.endElement(uri, localName, qName);
      }
    }
    catch (Exception e) {
      throw new SAXException(e);
    }
  }

  /**
  * FOR TESTING ONLY
  */
  public static void main(String[] args) {
    try {
      if(args.length < 1) {
        System.out.println("Proper usage: RefWalker <xml-file> [<xsl-file>]\n");
      }
      else {
        String sXml = args[0];
        String sXsl = (args.length > 1) ? args[1] : null;

        TransformerFactory transFact = TransformerFactory.newInstance( );

        if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {

          SAXTransformerFactory saxTransFact = (SAXTransformerFactory) transFact;

          TransformerHandler transHand = null;

          // Use the identity XSL transformation or the one provided
          if( sXsl == null ) {
            transHand = saxTransFact.newTransformerHandler( );
          } else {
            transHand = saxTransFact.newTransformerHandler(
              new StreamSource(new File(sXsl)));
          }

          // Set the destination for the XSLT transformation
          transHand.setResult(new StreamResult(System.out));

          // Setup the reader
          XMLReader myReader = XMLReaderFactory.createXMLReader(DEFAULT_SAX_PARSER);

          // Setup another "walker" to process this ref
          RefWalker refFilter = new RefWalker( myReader );

          // Setup the input source
          InputSource inputSource = new InputSource(
            new FileInputStream( new File(sXml)));

          // Attach the XSLT processor to the reader
          refFilter.setContentHandler(transHand);

          // Parse the new ref document
          refFilter.parse( inputSource );
        } else {
          System.err.println("SAXTransformerFactory is not supported.");
          System.exit(1);
        }
      }
    }
    catch (Exception e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }
}
0
 
LVL 27

Accepted Solution

by:
BigRat earned 200 total points
ID: 6894324
The XSLT transHand is attached to the first created instance of RefWalker, ie: the root instance. When you create a new reader and filter for the "included" file you need to attach something to that also (since the attachment is instance related and not static), and this needs to be done in startElement.
0
 
LVL 2

Author Comment

by:GleasonGuy
ID: 6894353
Thanks BigRat. You're really close to the actual solution I implemented yesterday morning. I wound up passing the TransformerHandler to the constructor. To sum up the changes...

--- snip ---
private TransformerHandler m_transHand;

public RefWalker(XMLReader xmlReader, TransformerHandler transHand) {
   super(xmlReader);
   m_transHand = transHand;
}
--- snip ---
public void startElement(...) {
    ...
    RefWalker refFilter = new RefWalker(myReader,m_transHand);
    ...
    refFilter.setContentHandler(m_transHand);
    refFilter.parse( inputSource );
}
--- snip ---
public static void main(...) {
    ...
    RefWalker refFilter = new RefWalker(myReader, transHand);
    ...
}
--- snip ---

Thanks,
GG
0
 
LVL 2

Author Comment

by:GleasonGuy
ID: 6894359
You're really more than close. It's actually right on to the solution I came up with. Thanks for the post. I hope others benefit from this as a PAQ.

Regards,
GG
0
 
LVL 27

Expert Comment

by:BigRat
ID: 6896055
Either a second constructor or a class static which gets set on startup. I don't like class statics so a second constructor is fine.
0
 
LVL 2

Author Comment

by:GleasonGuy
ID: 6897164
That's pretty funny. Are yo looking at my production code. ;-)

I actually have both a private constructor that gets called internally for all the nested parses AND a static class field to hold the common TransformerHandler. I'm not concerned about class fields in my case since the class is a very specialized process that will (should) never get instantiated more than once per run of the application.

Thanks,
GG
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Removing information on Duplicate XML Nodes 24 83
XML to SQL Table using c# 5 73
Create XML 5 52
what are list of ebay api errors 1 35
The Confluence of Individual Knowledge and the Collective Intelligence At this writing (summer 2013) the term API (http://dictionary.reference.com/browse/API?s=t) has made its way into the popular lexicon of the English language.  A few years ago, …
Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…

726 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question