Search code examples
javaxmlsimple-framework

How do you access field annotations from a custom Converter with Simple?


I'm using the Simple (http://simple.sourceforge.net/) library to marshall/unmarshall XML data in Java. For some of my more complex data structures, I need to write my own Converters. For instance, say I have a List<List<String>> that I need to marshall. I've written the following:

class WorldObject {

   @Element(name="vector-names")
   @Convert(ListListConverter.class)
   private List<List<String>> vectorNames;

   /** Constructor and other details ... **/

}

Along with the ListListConverter (I've left out the unmarshaller for the moment):

class ListListConverter implements Converter<List<List<String>>> {

   @Override
   public List<List<String>> read(InputNode node) throws Exception {
      // stub
      return null;
   }   

   @Override
   public void write(OutputNode node, List<List<String>> value)
         throws Exception {

      node.setName("list-list-string");

      for (List<String> list : value) {
         OutputNode subList = node.getChild("list-string");

         for (String str : list) {
            OutputNode stringNode = subList.getChild("string");
            stringNode.setValue(str);
         }

         subList.commit();
      }

      node.commit();

   }

}

This setup works fine, and produces the XML I want. I would, however, like to have access to the @Element annotation's name field so that I can give the tags the specified name (in this case, "vector-names") rather than the default name ("list-list-string"). This is how marshalling works for all the types that Simple handles out of the box, so there must be a way to access that data from a custom Converter.

How can I accomplish this?


Solution

  • You can't get the annotation that way, because it's not accessible through the field in the field-converter.
    The solution is to write a WorldObject-Converter - even if you only want to write a single field.

    WorldObject class:

    @Root
    @Convert(WorldObjectConverter.class) // specify converter for this class
    public class WorldObject
    {
        @Element(name = "vector-names")
        private List<List<String>> vectorNames;
    
        // only for the example below - write whatever constructor(s) you need
        public WorldObject()
        {
            this.vectorNames = new ArrayList<>();
        }
    
        // constructor, getter / setter, etc.
    
    
        // a getter is required to access the field in the converter.
        public List<List<String>> getVectorNames()
        {
            return vectorNames;
        }
    }
    

    WorldObjectConverter class:

    public class WorldObjectConverter implements Converter<WorldObject>
    {
        @Override
        public WorldObject read(InputNode node) throws Exception
        {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    
    
        @Override
        public void write(OutputNode node, WorldObject value) throws Exception
        {
            final Field f = value.getClass().getDeclaredField("vectorNames"); // get the field 'vectorNames' of the 'WorldObject' class
            final Element elementAnnotation = f.getAnnotation(Element.class); // get the 'Element' annotation of the Field
    
            final String name = elementAnnotation.name(); // get the 'name'-value of the annotation
            node.setName(name); // set Nodename
    
    
            for( List<String> list : value.getVectorNames() )
            {
                OutputNode subList = node.getChild("list-string");
    
                for( String str : list )
                {
                    OutputNode stringNode = subList.getChild("string");
                    stringNode.setValue(str);
                }
    
                subList.commit();
            }
    
            node.commit();
        }
    }
    

    Example:

    final File f = new File("test.xml"); // output file
    
    WorldObject wo = new WorldObject(); // the object to serialize
    
    // some testdata ...
    List<String> l = new ArrayList<>();
    l.add("a");
    l.add("b");
    wo.getVectorNames().add(l);
    
    l = new ArrayList<>();
    l.add("c");
    l.add("d");
    wo.getVectorNames().add(l);
    
    
    // create the serializer - dont forget the AnnotationStrategy!
    Serializer ser = new Persister(new AnnotationStrategy());
    ser.write(wo, f); // serialize it to file
    

    Output:

    <vector-names>
       <list-string>
          <string>a</string>
          <string>b</string>
       </list-string>
       <list-string>
          <string>c</string>
          <string>d</string>
       </list-string>
    </vector-names>
    

    Done!