United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4864117 RFE: Make XMLDecoder API more reusable
JDK-4864117 : RFE: Make XMLDecoder API more reusable

Details
Type:
Enhancement
Submit Date:
2003-05-14
Status:
Closed
Updated Date:
2013-11-08
Project Name:
JDK
Resolved Date:
2011-03-08
Component:
client-libs
OS:
linux
Sub-Component:
java.beans
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.0,1.4.2
Fixed Versions:

Related Reports
Relates:
Relates:
Relates:

Sub Tasks

Description
Name: rmT116609			Date: 05/14/2003


A DESCRIPTION OF THE REQUEST :
The XMLDecoder is a very useful unit of code. It is unfortunate that it is very inflexible in its use and API. The main problem lies with the use of an InputStream as the primary source of XML for parsing. In addition, the package protected nature of the XMLDecoders internals makes it impossible to customize without rewriting the entire beans package (which is bad because so much is static).

JUSTIFICATION :
If XML persisted beans reside in another XML document, in order to decode them one must either:
a) store them in a CDATA block, buffer the characters, turn them into bytes and then create an XMLDecoder using a ByteArrayInputStream
b) store them as normal elements in the document (taking care to remove the redundant <?xml version> element that the XMLEncoder places everytime it writes), reconstruct elements from the handler, buffer the elements, turn them into bytes, then create an XMLDecoder using ByteArrayInputStream

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
1) Make XMLEncoder flexible so that it does not always output the ever-so-useful <?xml version> element
2) Make XMLDecoder take a Reader as an argument so that if the data is ALREADY in character form, I don't have to waste time and memory converting it to bytes...
3) Restructure XMLDecoder so that sub-classes can have access to some internals. I understand the desire to keep JAXP classes out of the public XMLDecoder API, but for subclasses, access to a ContentHandler would make dealing with embedded bean documents much easier.
ACTUAL -
If one wants to embed a XMLEncoded bean in another document, one must take care to either
a) write the persisted bean within a CDATA section
b) remove the <?xml version> element the XMLEncoder produces

If one wants to decode an element which represents an encoded bean, one must buffer all the content, turn it into bytes, and pass the bytes to the XMLDecoder.

---------- BEGIN SOURCE ----------
//Encoder for embedding XML persisted beans in document
import java.beans.XMLEncoder;
import java.io.*;
public class MixedEncoder {
  
  PrintStream writer;
  TrickyOutputStream tricky;
  
  static final class TrickyOutputStream extends FilterOutputStream {
    boolean ignore = false;
    public TrickyOutputStream(OutputStream out) {
      super(out);
    }
    
    public void write(byte[] b) throws IOException {
      if (ignore) return;
      out.write(b);
    }
    
    public void write(byte[] b,int off,int len) throws IOException {
      if (ignore) return;
      out.write(b,off,len);
    }
    
    public void write(int i) throws IOException {
      if (ignore) return;
      out.write(i);
    }
    
    public void close() throws IOException {
       
    }
  }
  
  /** Creates a new instance of MixedEncoder */
  public MixedEncoder(OutputStream out) throws Exception {
    writer = new PrintStream(out,false,"UTF-8");
    tricky = new TrickyOutputStream(writer);
    writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
    writer.println("<specialDocument>");
  }
  
  public void writeObject(Object o,String extra) {
    writer.println("<special extra=\"" + extra + "\">");
    writer.println("<java>");
    XMLEncoder encoder = new XMLEncoder(tricky);
    tricky.ignore = true;
    encoder.flush();
    tricky.ignore = false;
    encoder.writeObject(o);
    encoder.flush();
    encoder.close();
    writer.println("</special>");
  }
  
  public void flush() {
    writer.flush();
  }
  
  public void close() {
    writer.println("</specialDocument>");
    writer.flush();
    writer.close();
  }
}

// Class for decoding from mixed document
import java.beans.*;
import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class MixedDecoder {
  
  /** Creates a new instance of MixedDecoder */
  public MixedDecoder(InputStream in) throws Exception {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    SAXParser parser = factory.newSAXParser();
    try {
      MixedHandler mh = new MixedHandler();
      parser.parse(in,mh);
    } catch (SAXParseException spe) {
      spe.getException().printStackTrace();
    }
  }
  
  static class MixedHandler extends DefaultHandler {
    
    String message;
    StringBuffer buffer;
    boolean special = false;
    
    public void startElement(String namespaceURI, String localName, String qName, org.xml.sax.Attributes atts) throws org.xml.sax.SAXException {
      if (special)
        appendStart(qName,atts);
      else if (qName.equals("special")) {
        message = atts.getValue("extra");
        special = true;
        buffer = new StringBuffer();
      }
    }
    
    private void appendStart(String qn,Attributes atts) {
      buffer.append('<').append(qn);
      if (atts.getLength() > 0)
        buffer.append(' ');
      for (int i = 0, ii = atts.getLength(); i < ii; i++) {
        buffer.append(atts.getQName(i)).append("=\"").append(atts.getValue(i)).append("\"");
      }
      buffer.append('>');
    }
    
    public void characters(char[] ch, int start, int length) throws org.xml.sax.SAXException {
      if (special) {
        while (start < ch.length && Character.isWhitespace(ch[start])) {start++; length--;}
        buffer.append(ch,start,length);
      }
    }
    
    public void endElement(String namespaceURI, String localName, String qName) throws org.xml.sax.SAXException {
      
      if (qName.equals("special")) {
        XMLDecoder decoder = new XMLDecoder(new ByteArrayInputStream(buffer.toString().getBytes()));
        if (decoder.readObject() == null)
          System.out.println("bogus");
        buffer = null;
        special = false;
      }
      else if (special)
        appendEnd(qName);
    }
    
    private void appendEnd(String qn) {
      buffer.append("</").append(qn).append(">\n");
    }
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The hacks presented in the two classes provide a work around for this functionality.
(Review ID: 185723) 
======================================================================

                                    

Comments
EVALUATION

If the customer wants to remove the <?xml version> declaration
he can use the following public constructor since Java 7
XMLEncoder(OutputStream out, String charset, boolean declaration, int indentation)
* @param declaration  whether the XML declaration should be generated;
*                     set this to <code>false</code>
*                     when embedding the contents in another XML document
                                     
2007-06-05



Hardware and Software, Engineered to Work Together