XStream
  1. XStream
  2. XSTR-308

Conditional Conversion support

    Details

    • Type: New Feature New Feature
    • Status: Open Open
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None

      Description

      So i've rethought my need to not serialize things like empty collections and as well have seen other requests on the list from people who don't want to serialize int's that are zero, empty strings, etc.

      This patch allows the converter to make the call on weather the data (and enclosing tag) is written or not. I added this functionality as an additional and optional interface, ConditionalConverter, rather than adding it directly to Converter and SingleValueConverter as I don't want to break any custom Converter implementations people have out there in the wild. If it's more desirable to add the method right to those interfaces, that's fine too.

      For the sake of ease of review, here is a chuck of an attached test case:

          public static class SkipEmptyCollectionConverter extends CollectionConverter implements ConditionalConverter {
              public SkipEmptyCollectionConverter(Mapper mapper) {
                  super(mapper);
              }
      
              public boolean shouldConvert(Class type, Object value) {
                  return (value != null && ((Collection) value).size() != 0);
              }
          }
      
          public static class SkipEmptyMapConverter extends MapConverter implements ConditionalConverter {
              public SkipEmptyMapConverter(Mapper mapper) {
                  super(mapper);
              }
      
              public boolean shouldConvert(Class type, Object value) {
                  return (value != null && ((Map) value).size() != 0);
              }
          }
      
          public static class SkipEmptyStringConverter extends StringConverter {
              public boolean shouldConvert(Class type, Object value) {
                  return (value != null && ((String)value).length() != 0);
              }
          }
      
          public static class Contact extends StandardObject {
              public String name;
              public String company;
              public List emailAddresses = new ArrayList();
              public Map addresses = new HashMap();
              public Map telephone = new HashMap();
              public String notes;
      
              public Contact(String name) {
                  this.name = name;
              }
          }
      
          public void testSkipNonSimpleTypes() {
              List addressBook = new ArrayList();
              Contact wallace = new Contact("Wallace");
              wallace.company = "Window Washing Inventors, Inc.";
              wallace.addresses.put("home", "62 West Wallaby Street");
              addressBook.add(wallace);
      
              Contact gromit = new Contact("Gromit");
              gromit.emailAddresses.add("gromit@genious-k9s.net");
              addressBook.add(gromit);
      
              Contact jenny = new Contact("Jenny");
              jenny.telephone.put("home", "867-5309");
              jenny.notes = "Found this number on the bathroom wall.";
              addressBook.add(jenny);
      
              xstream.alias("contact", Contact.class);
              xstream.registerConverter(new SkipEmptyCollectionConverter(xstream.getMapper()));
              xstream.registerConverter(new SkipEmptyMapConverter(xstream.getMapper()));
              xstream.registerConverter(new SkipEmptyStringConverter());
      
              String expectedXml = ""+
                      "<list>\n" +
                      "  <contact>\n" +
                      "    <name>Wallace</name>\n" +
                      "    <company>Window Washing Inventors, Inc.</company>\n" +
                      "    <addresses>\n" +
                      "      <entry>\n" +
                      "        <string>home</string>\n" +
                      "        <string>62 West Wallaby Street</string>\n" +
                      "      </entry>\n" +
                      "    </addresses>\n" +
                      "  </contact>\n" +
                      "  <contact>\n" +
                      "    <name>Gromit</name>\n" +
                      "    <emailAddresses>\n" +
                      "      <string>gromit@genious-k9s.net</string>\n" +
                      "    </emailAddresses>\n" +
                      "  </contact>\n" +
                      "  <contact>\n" +
                      "    <name>Jenny</name>\n" +
                      "    <telephone>\n" +
                      "      <entry>\n" +
                      "        <string>home</string>\n" +
                      "        <string>867-5309</string>\n" +
                      "      </entry>\n" +
                      "    </telephone>\n" +
                      "    <notes>Found this number on the bathroom wall.</notes>\n" +
                      "  </contact>\n" +
                      "</list>";
      
              assertBothWays(addressBook, expectedXml);
          }
      
      1. ConditionalConversion.patch
        24 kB
        David Blevins
      2. FieldAliasingMapper-shouldSerializeMember.patch
        0.6 kB
        David Blevins
      3. FieldAliasingMapper-shouldSerializeMember2.patch
        0.8 kB
        David Blevins
      4. OmitFieldsTest-shouldSerializeMember.patch
        0.6 kB
        David Blevins

        Activity

        Hide
        David Blevins added a comment -

        Missed one overloaded call to shouldSerializeMember in FieldAliasingMapper. The FieldAliasingMapper-shouldSerializeMember.patch should do the trick.

        Show
        David Blevins added a comment - Missed one overloaded call to shouldSerializeMember in FieldAliasingMapper. The FieldAliasingMapper-shouldSerializeMember.patch should do the trick.
        Hide
        David Blevins added a comment -

        One more try

        Show
        David Blevins added a comment - One more try
        Hide
        Jörg Schaible added a comment -

        A comment from Joe about this from some time ago (so that it's not forgotten):

        I don't think this fits with the responsibility of a Converter.

        • A Mapper is used by the parent Converter to determine any special
          rules about if a child should be serialized and how it should be
          represented to the parent.
        • The child Converter is then used for serializing the details of the child.

        I think this change will overload the responsibility of the Converter.
        I would prefer to see the Mapper taking care of this and a new
        interface added if necessary.

        eg.

        xstream.registerFilter(new IgnoreIntConverter()):

        Show
        Jörg Schaible added a comment - A comment from Joe about this from some time ago (so that it's not forgotten): I don't think this fits with the responsibility of a Converter. A Mapper is used by the parent Converter to determine any special rules about if a child should be serialized and how it should be represented to the parent. The child Converter is then used for serializing the details of the child. I think this change will overload the responsibility of the Converter. I would prefer to see the Mapper taking care of this and a new interface added if necessary. eg. xstream.registerFilter(new IgnoreIntConverter()):

          People

          • Assignee:
            Unassigned
            Reporter:
            David Blevins
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: