Jettison
  1. Jettison
  2. JETTISON-36

String values containing only decimal digits are serialized as integer values

    Details

    • Type: Bug Bug
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.0
    • Fix Version/s: 1.1
    • Labels:
      None
    • Environment:
      Java 5; xstream 1.2.2; junit 1.3.8
    • Testcase included:
      yes
    • Number of attachments :
      1

      Description

      A string value containing only decimal digits is serialized as integer value.

      The problem with this is that in some cases some characters of string values are not serialized to JSON. Leading zero's of a string value containing only decimal digits are not serialized, for instance.

      In version 1.0-RC1 of Jettison an instance of a class having a member variable (svalue in this example) of type String with a value of "00001234" was serialized as follows:
      {"TestClassWithString":{"svalue":"00001234"}}

      In version 1.0 of Jettison the an instance of the same class holding the same value is serialized as follows:
      {"TestClassWithString":{"svalue":1234}}

      Note that the leading zero's are lost in the latest version. In my case the leading zero's are important as the string holds a reference to an entity in an external system.

      A JUnit test (xstream based) for this case is attached.

        Activity

        Hide
        Doug Daniels added a comment -

        I believe this is a side effect of the fix for:
        http://jira.codehaus.org/browse/JETTISON-32

        The fix attempted to convert strings that appear to be JSON primitives (e.g. int, boolean) as JSON numeric literals.

        The problem is without this fix then everything is serialized to JSON as a string. The trouble in converting that is XStream only sends the values to the JSON converter as strings so we lose the type information and have to try to figure it out.

        It's kind of hacky but maybe some compromise could be made where a string that "could" be a numeric value but contains things like leading 00's is converted as a string. I guess any time a string value could not be converted to a numeric value and then back to string and still be the same, then it should be a string. Maybe something like:
        private Object convertToJSONPrimitive(String text) {
        //....
        if (primitive == null || !text.equals(primitive+""))

        { // Default String primitive = text; }
        return primitive;
        }

        Full method:
        (MappedXMLStreamWriter.java org.codehaus.jettison.mapped)

        private Object convertToJSONPrimitive(String text) {

        Object primitive = null;
        // Attempt to convert to Integer
        try { primitive = Long.valueOf(text); } catch (Exception e) {
        }
        // Attempt to convert to double
        if (primitive == null) {
        try { primitive = Double.valueOf(text); } catch (Exception e) {
        }
        }
        // Attempt to convert to boolean
        if (primitive == null) {
        if(text.trim().equalsIgnoreCase("true") || text.trim().equalsIgnoreCase("false")) { primitive = Boolean.valueOf(text); }
        }

        if (primitive == null || !text.equals(primitive+"")) { // Default String primitive = text; }

        return primitive;
        }

        Show
        Doug Daniels added a comment - I believe this is a side effect of the fix for: http://jira.codehaus.org/browse/JETTISON-32 The fix attempted to convert strings that appear to be JSON primitives (e.g. int, boolean) as JSON numeric literals. The problem is without this fix then everything is serialized to JSON as a string. The trouble in converting that is XStream only sends the values to the JSON converter as strings so we lose the type information and have to try to figure it out. It's kind of hacky but maybe some compromise could be made where a string that "could" be a numeric value but contains things like leading 00's is converted as a string. I guess any time a string value could not be converted to a numeric value and then back to string and still be the same, then it should be a string. Maybe something like: private Object convertToJSONPrimitive(String text) { //.... if (primitive == null || !text.equals(primitive+"")) { // Default String primitive = text; } return primitive; } Full method: (MappedXMLStreamWriter.java org.codehaus.jettison.mapped) private Object convertToJSONPrimitive(String text) { Object primitive = null; // Attempt to convert to Integer try { primitive = Long.valueOf(text); } catch (Exception e) { } // Attempt to convert to double if (primitive == null) { try { primitive = Double.valueOf(text); } catch (Exception e) { } } // Attempt to convert to boolean if (primitive == null) { if(text.trim().equalsIgnoreCase("true") || text.trim().equalsIgnoreCase("false")) { primitive = Boolean.valueOf(text); } } if (primitive == null || !text.equals(primitive+"")) { // Default String primitive = text; } return primitive; }
        Hide
        Arjen Wassink added a comment -

        Thanks Doug,

        The proposed code change solves my problem.

        I had a further look at the MappedXMLStreamWriter class and found out that the proposed change is generally OK. It gives a watertight solution to the loss of information problem in the marshall/unmarshall process and preserves the marshalling to JSON primitives. (This within the context that the MappedXMLStreamWriter class has no notion of the (class)type of the source value.

        As you already noticed Doug, getting type information from xstream would generally be a better sollution...

        Show
        Arjen Wassink added a comment - Thanks Doug, The proposed code change solves my problem. I had a further look at the MappedXMLStreamWriter class and found out that the proposed change is generally OK. It gives a watertight solution to the loss of information problem in the marshall/unmarshall process and preserves the marshalling to JSON primitives. (This within the context that the MappedXMLStreamWriter class has no notion of the (class)type of the source value. As you already noticed Doug, getting type information from xstream would generally be a better sollution...
        Hide
        Dejan Bosanac added a comment -

        Type conversion mechanism is implemented, which allows users to control this behavior. There are two implemented converters

        • simple converter, which treats everything as a string
        • default converter, which works as now

        Users that has problems with leading zeros can try simple converter. Example:

        //issue 36
        public void testConverter() throws Exception {
        StringWriter strWriter = new StringWriter();
        Configuration config = new Configuration();
        config.setTypeConverter(new SimpleConverter());
        MappedNamespaceConvention con = new MappedNamespaceConvention(config);
        AbstractXMLStreamWriter w = new MappedXMLStreamWriter(con, strWriter);

        w.writeStartElement("root");
        w.writeCharacters("000123");
        w.writeEndElement();

        w.writeEndDocument();

        w.close();
        strWriter.close();

        System.out.println(strWriter.toString());

        assertEquals("

        {\"root\":\"000123\"}

        ", strWriter.toString());
        }

        available in the following snapshot:

        http://snapshots.repository.codehaus.org/org/codehaus/jettison/jettison/1.1-SNAPSHOT/jettison-1.1-20080424.080632-3.jar

        Show
        Dejan Bosanac added a comment - Type conversion mechanism is implemented, which allows users to control this behavior. There are two implemented converters simple converter, which treats everything as a string default converter, which works as now Users that has problems with leading zeros can try simple converter. Example: //issue 36 public void testConverter() throws Exception { StringWriter strWriter = new StringWriter(); Configuration config = new Configuration(); config.setTypeConverter(new SimpleConverter()); MappedNamespaceConvention con = new MappedNamespaceConvention(config); AbstractXMLStreamWriter w = new MappedXMLStreamWriter(con, strWriter); w.writeStartElement("root"); w.writeCharacters("000123"); w.writeEndElement(); w.writeEndDocument(); w.close(); strWriter.close(); System.out.println(strWriter.toString()); assertEquals(" {\"root\":\"000123\"} ", strWriter.toString()); } available in the following snapshot: http://snapshots.repository.codehaus.org/org/codehaus/jettison/jettison/1.1-SNAPSHOT/jettison-1.1-20080424.080632-3.jar

          People

          • Assignee:
            Dejan Bosanac
            Reporter:
            Arjen Wassink
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: