Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Howto extend org.simpleframework.xml with an annotation that will write comments into the resulting XML

I am using org.simpleframework.xml (http://simple.sourceforge.net/) to serialize Java Objects to XML.

What I would like to add is to add a comments area in the resulting XML, based on Annotations in the Java object.

So for example I would like to write some Java Object like:

@Root(name = "myclass")
public class MyClass {
  @Element(required=true)
  @Version(revision=1.1)
  @Comment(text=This Element is new since, version 1.1, it is a MD5 encrypted value)
  private String activateHash;
}

And the resulting xml would look like:

<myclass version="1.1">
  <!-- This Element is new since, version 1.1, it is a MD5 encrypted value -->
  <activateHash>129831923131s3jjs3s3jjk93jk1</activateHash>
</myclass>

There is an example in their docs on howto write a Visitor that will write a comments in the xml: http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php#intercept

However: How can I attach a Visitor to a Strategy at all?

And further the Visitor concept of simpleframework does not allow access to the raw parsing class. In the Visitor there is only a method to overwrite:

public void write(Type type, NodeMap<OutputNode> node) { ... }

=> OutputNode does not give me a chance to read the Annotation of the Element that I am parsing. So how should one access the Annotations of the attribute.

Thanks!

Sebastian

like image 738
seba.wagner Avatar asked Oct 22 '25 00:10

seba.wagner


1 Answers

Update as of 2012-11-05:

Answer by the author of org.simpleframework.xml: This works

https://simple.svn.sourceforge.net/svnroot/simple/trunk/download/stream/src/test/java/org/simpleframework/xml/strategy/CommentTest.java

package org.simpleframework.xml.strategy;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.simpleframework.xml.Default;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.ValidationTestCase;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.stream.InputNode;
import org.simpleframework.xml.stream.NodeMap;
import org.simpleframework.xml.stream.OutputNode;

public class CommentTest extends ValidationTestCase {

   @Retention(RetentionPolicy.RUNTIME)
   private static @interface Comment {
      public String value();
   }

   @Root
   @Default
   private static class CommentExample {
      @Comment("This represents the name value")
      private String name;
      @Comment("This is a value to be used")
      private String value;
      @Comment("Yet another comment")
      private Double price;
   }

   private static class CommentVisitor implements Visitor {
      public void read(Type type, NodeMap<InputNode> node) throws Exception {}
      public void write(Type type, NodeMap<OutputNode> node) throws Exception {
         if(!node.getNode().isRoot()) {
            Comment comment = type.getAnnotation(Comment.class);
            if(comment != null) {
               node.getNode().setComment(comment.value());
            }
         }
      }
   }

   public void testComment() throws Exception {
      Visitor visitor = new CommentVisitor();
      Strategy strategy = new VisitorStrategy(visitor);
      Persister persister = new Persister(strategy);
      CommentExample example = new CommentExample();

      example.name = "Some Name";
      example.value = "A value to use";
      example.price = 9.99;

      persister.write(example, System.out);
   }

}

Update as of 2012-11-01 20:16

this is the workaround that seems to get the desired effect - the necessary FieldHelper is described in (Get the value of a field, given the hierarchical path)

    /**
     * write according to this visitor
     */
    public void write(Type type, NodeMap<OutputNode> node) {
        OutputNode element = node.getNode();
        Class ctype = type.getType();

        String comment = ctype.getName();
        if (!element.isRoot()) {
            FieldHelper fh = new FieldHelper();
            element.setComment(comment);
            try {
                if (type.getClass().getSimpleName().startsWith("Override")) {
                    type = (Type) fh.getFieldValue(type, "type");
                }
                if (type.getClass().getSimpleName().startsWith("Field")) {
                    Field field = (Field) fh.getFieldValue(type, "field");
                    System.out.println(field.getName());
                    Comment commentAnnotation = field.getAnnotation(Comment.class);
                    if (commentAnnotation != null) {
                        element.setComment(commentAnnotation.value());
                    }
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

Here is how far I got with this. Unfortunately it does not work as expected. I have written an E-Mail to the author of the Simpleframwork for XML.

  /**
  * write according to this visitor
  */
    public void write(Type type, NodeMap<OutputNode> node) {
        OutputNode element = node.getNode();
        Class ctype = type.getType();

        String comment = ctype.getName();
        if (!element.isRoot()) {
            Comment commentAnnotation = type.getAnnotation(Comment.class);
            if (commentAnnotation!=null)
                element.setComment(commentAnnotation.value());
            else
                element.setComment(comment);
        }
    }

    @Override
    public void read(Type type, NodeMap<InputNode> nodeMap) throws Exception {

    }

}

I declared the Comment annotation like this:

package com.bitplan.storage.simplexml;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Comment {
String value();
}

which is then usable like this:

@Comment("this is the unique identifier")
private long id;

adding the Visitor was possible like this:

/**
 * get Serializer
 * 
 * @return
 */
public Serializer getSerializer() {
    Serializer serializer = null;
    Strategy strategy=null;
    VisitorStrategy vstrategy=null;
    if ((idname != null) && (refname != null)) {
        strategy = new CycleStrategy(idname, refname);
    }
    CommentVisitor cv=new CommentVisitor();
    if (strategy==null) {
        vstrategy=new VisitorStrategy(cv);
    } else {
        vstrategy=new VisitorStrategy(cv,strategy);
    }       
    serializer = new Persister(vstrategy);
    return serializer;
}
like image 152
Wolfgang Fahl Avatar answered Oct 23 '25 16:10

Wolfgang Fahl