users@jaxb.java.net

Re: Marshalling in chunks?

From: <eguy66_at_optonline.net>
Date: Fri, 19 Aug 2005 14:33:47 -0400

Thanks!!!
Between your explanation, and the documentation at http://www-128.ibm.com/developerworks/xml/library/x-tipbigdoc5.html, I was able to implement an XMLFilter to marshall in chunks and suppress namespace definititions on the "root" subnodes.

The least intuitive part for me was realizing that I had to set the content handler and the error handler of my filter to the very same DataWriter that I was filtering. Before making those settings, I was trapping events, but not producing any output.

My resulting code is something like this:

    DataWriter dw = new DataWriter(new FileWriter("out.xml"),"utf-8");
    dw.setIndentStep(" ");
    MyXMLFilter mx = new MyXMLFilter(dw);
    mx.setContentHandler(dw);
    mx.setErrorHandler(dw);
    Envelope info = objFactory.createEnvelope();
    info.setIndicator1(true);
    info.setIndicator2(false);
    marshaller.marshal(info, mx);

The filter class:

class MyXMLFilter
    extends XMLFilterImpl {
  private Marshaller m;
  private ObjectFactory o;
  private boolean nested = false;

  public MyXMLFilter(XMLReader parent) throws JAXBException {
    super(parent);
    m = JAXBContext.newInstance("{jaxbPackage}").createMarshaller();
    m.setProperty("com.sun.xml.bind.namespacePrefixMapper",
                  new NamespacePrefixMapperImpl());
    o = new ObjectFactory();
  }

  public void startDocument() throws SAXException {
    if (!nested) {
      super.startDocument();
    }
  }

  public void endDocument() throws SAXException {
    if (!nested) {
      super.endDocument();
    }
  }

  public void startPrefixMapping(String prefix, String uri) throws SAXException {
    if (!nested) {
      super.startPrefixMapping(prefix, uri);
    }
  }

  public void endPrefixMapping(String prefix) throws SAXException {
    if (!nested) {
      super.endPrefixMapping(prefix);
    }
  }

  public void endElement(String uri, String localName, String qName) throws
      SAXException {
    if ("Marsh:Envelope".equals(qName)) {
      nested = true;
      // loop to do fragment marshalling here
        m.marshal(info, this);
      // end loop
      nested = false;
    }
    super.endElement(uri, localName, qName);
  }
}


Thanks again,
Ed



----- Original Message -----
Date: Wed, 17 Aug 2005 17:17:11 -0700
From: Kohsuke Kawaguchi <Kohsuke.Kawaguchi_at_Sun.COM>
Content-type: multipart/signed; protocol="application/x-pkcs7-signature";
 micalg=sha1; boundary=------------ms090708040003090609080603
Subject: Marshalling in chunks?

eguy66_at_optonline.net wrote:
> Is my assumption that JAXB 2.0 requires jdk 1.5 correct?

Yes.

> If my above assumption is correct, I'd very much like to see a sample
> SAX XMLFilter, if you can point me to one :) I tried tinkering with
> the generated SAXMarshaller (pass in the printXmlDeclaration value from
> MarshallerImpl) to control the NamespaceContextImpl, without much
> success.

It's probably difficult if you haven't done any SAX programming.

Suppose you want to write:

<envelope>
   <header> ... </header>
   <header> ... </header>
   ...

   <body> ... </body>
   <body> ... </body>
   <body> ... </body>
   ...
</envelope>

where you have numerous huge <body>s so you want to create it and
marshal it one by one.

The idea is to set up the writing process as:

        Marshaller -> YourSAXFilter -> DataWriter

See org.xml.sax.helpers.XMLFilterImpl. By extending it, you can
intercept all the elements that JAXB writes. You can then simply look
for the element where you want to inject the big portion.

You first build the JAXB objects that correspond to:

<envelope>
   <header> ... </header>
   <header> ... </header>
   ...
</envelope>

and then marshal it as shown above. Your XMLFilterImpl would look for
the end element of <envelope>. If it hits one, you goes into the loop
right there.

class MyXMLFilter {
   void endElement(...) {
     if(tagName is "envelope") {
       Marshaller m = create2ndMarshaller();
       enable fragment support with m;
       loop {
         body = createNewBody();
         m.marshal(body,this);
       }
     }
   }
}

The marshal method then marshals a body object to the same filter,
causing the output from <body>s to be merged into DataWriter.

You need to mask the nested start/endDocument events from the 2nd
marshaller, so that DataWriter won't get confused.

-- 
Kohsuke Kawaguchi
Sun Microsystems                   kohsuke.kawaguchi_at_sun.com