Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8171195

JAX-B Marshal/Unmarshal not symmetric for Strings with newline characters CR LF

XMLWordPrintable

    • generic
    • generic

      FULL PRODUCT VERSION :
      java version "1.8.0_112"
      Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
      Java HotSpot(TM) Client VM (build 25.112-b15, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      JAX-B marshal/unmarshal operations are not symmetric for strings containing newline characters (CR, LF or CRLF) (i.e. the resulting string after a marshal/unmarshal cycle isn't what the original string was).
      Since XML parsers must normalize attribute values by replacing newline characters with spaces (reference: http://www.w3.org/TR/REC-xml/#AVNormalize), newline characters need to be escaped properly during marshalling. This escaping seems to be missing in JAX-B. In other parts of the JDK, this is handled properly, e.g. Transformer properly escapse newline characters.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See attached test case. The 'testWriter' method shows the problem.


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.StringReader;
      import java.io.StringWriter;

      import javax.xml.bind.JAXBContext;
      import javax.xml.bind.Marshaller;
      import javax.xml.bind.Unmarshaller;
      import javax.xml.bind.annotation.XmlAccessType;
      import javax.xml.bind.annotation.XmlAccessorType;
      import javax.xml.bind.annotation.XmlAttribute;
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlRootElement;
      import javax.xml.bind.annotation.XmlType;
      import javax.xml.parsers.DocumentBuilder;
      import javax.xml.parsers.DocumentBuilderFactory;
      import javax.xml.transform.Transformer;
      import javax.xml.transform.TransformerFactory;
      import javax.xml.transform.dom.DOMResult;
      import javax.xml.transform.dom.DOMSource;
      import javax.xml.transform.stream.StreamResult;

      import junit.framework.TestCase;

      import org.w3c.dom.Document;

      public class CrLfMarshalUnmarshalTest extends TestCase {
        private static final String VALUE = "abc\r\nd\re\nf";
        private Bean fElem;
        private Marshaller fMarshaller;
        private Unmarshaller fUnmarshaller;
        
        @Override
        protected void setUp() throws Exception {
          super.setUp();
          JAXBContext ctx = JAXBContext.newInstance(Bean.class);
          fMarshaller = ctx.createMarshaller();
          fUnmarshaller = ctx.createUnmarshaller();
          
          fElem = new Bean();
          fElem.setAttributeString(VALUE);
          fElem.setElementString(VALUE);
        }
        
        public void testWriter() throws Exception {
          StringWriter sw = new StringWriter();
          fMarshaller.marshal(fElem, sw);
          String resultXml = sw.toString();
          System.out.println(resultXml);
          Bean resultElem = (Bean) fUnmarshaller.unmarshal(new StringReader(resultXml));
          assertEquals(VALUE, resultElem.getAttributeString());
          assertEquals(VALUE, resultElem.getElementString());
        }

        public void testDomAndTransform() throws Exception {
          StringWriter sw = new StringWriter();
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          dbf.setNamespaceAware(true);
          DocumentBuilder db = dbf.newDocumentBuilder();
          Document d = db.newDocument();
          DOMResult result = new DOMResult(d);
          fMarshaller.marshal(fElem, result);
          Transformer tf = TransformerFactory.newInstance().newTransformer();
          tf.transform(new DOMSource(d), new StreamResult(sw));
          String resultXml = sw.toString();
          System.out.println(resultXml);
          Bean resultElem = (Bean) fUnmarshaller.unmarshal(new StringReader(resultXml));
          assertEquals(VALUE, resultElem.getAttributeString());
          assertEquals(VALUE, resultElem.getElementString());
        }
        
        @XmlAccessorType(XmlAccessType.FIELD)
        @XmlType(name = "", propOrder = {"fElementString", "fAttributeString"})
        @XmlRootElement(name = "Bean")
        public static class Bean {

          @XmlElement(name = "ElementString")
          protected String fElementString;

          @XmlAttribute(name = "AttributeString")
          protected String fAttributeString;
          
          public String getElementString() {
            return fElementString;
          }
          
          public void setElementString(String elementString) {
            fElementString = elementString;
          }
          
          public String getAttributeString() {
            return fAttributeString;
          }
          
          public void setAttributeString(String attributeString) {
            fAttributeString = attributeString;
          }

        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      See attached test case. The 'testDomAndTransform' method shows a workaround: the actual xml output is delegated to a Transformer, which properly escapes newline characters.
       

            dkral David Kral
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: