← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 15591: rewrote serializers to use a common (abstract) base class

 

------------------------------------------------------------
revno: 15591
committer: Morten Olav Hansen <mortenoh@xxxxxxxxx>
branch nick: dhis2
timestamp: Sun 2014-06-08 12:58:50 +0200
message:
  rewrote serializers to use a common (abstract) base class
removed:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/JacksonJsonNodeSerializer.java
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNodeSerializer.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Deserializer.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Serializer.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/Jackson2JsonNodeSerializer.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNode.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Node.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeDeserializer.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeSerializer.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/StAXNodeSerializer.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/types/SimpleNode.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/node/DefaultNodeService.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNode.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNode.java	2014-06-06 07:40:49 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNode.java	2014-06-08 10:58:50 +0000
@@ -28,6 +28,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
  */
 
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.hisp.dhis.node.exception.InvalidTypeException;
 import org.hisp.dhis.node.types.SimpleNode;
@@ -42,15 +44,17 @@
  */
 public abstract class AbstractNode implements Node
 {
-    private String name;
-
-    private final NodeType nodeType;
-
-    private String namespace;
-
-    private String comment;
-
-    private List<Node> children = Lists.newArrayList();
+    protected String name;
+
+    protected final NodeType nodeType;
+
+    protected Node parent;
+
+    protected String namespace;
+
+    protected String comment;
+
+    protected List<Node> children = Lists.newArrayList();
 
     protected AbstractNode( String name, NodeType nodeType )
     {
@@ -76,6 +80,17 @@
     }
 
     @Override
+    public Node getParent()
+    {
+        return parent;
+    }
+
+    protected void setParent( Node parent )
+    {
+        this.parent = parent;
+    }
+
+    @Override
     public boolean is( NodeType type )
     {
         return type.equals( nodeType );
@@ -130,6 +145,8 @@
         }
 
         children.add( child );
+        ((AbstractNode) child).setParent( this );
+
         return child;
     }
 
@@ -147,7 +164,7 @@
     {
         List<Node> clone = Lists.newArrayList( children );
         Collections.sort( clone, OrderComparator.INSTANCE );
-        return clone;
+        return ImmutableList.copyOf( clone );
     }
 
     @Override
@@ -196,12 +213,13 @@
     @Override
     public String toString()
     {
-        return "Node{" +
-            "name='" + name + '\'' +
-            ", nodeType=" + nodeType +
-            ", namespace='" + namespace + '\'' +
-            ", comment='" + comment + '\'' +
-            ", children=" + children +
-            '}';
+        return Objects.toStringHelper( this )
+            .add( "name", name )
+            .add( "nodeType", nodeType )
+            .add( "parent", parent.getName() )
+            .add( "namespace", namespace )
+            .add( "comment", comment )
+            .add( "children", children )
+            .toString();
     }
 }

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNodeSerializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNodeSerializer.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNodeSerializer.java	2014-06-08 10:58:50 +0000
@@ -0,0 +1,137 @@
+package org.hisp.dhis.node;
+
+/*
+ * Copyright (c) 2004-2014, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ */
+
+import org.hisp.dhis.node.types.CollectionNode;
+import org.hisp.dhis.node.types.ComplexNode;
+import org.hisp.dhis.node.types.RootNode;
+import org.hisp.dhis.node.types.SimpleNode;
+
+import java.io.OutputStream;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public abstract class AbstractNodeSerializer implements NodeSerializer
+{
+    protected boolean startSerialize( RootNode rootNode, OutputStream outputStream ) throws Exception
+    {
+        return true;
+    }
+
+    protected void endSerialize( RootNode rootNode, OutputStream outputStream ) throws Exception
+    {
+    }
+
+    protected abstract void flushStream() throws Exception;
+
+    @Override
+    public void serialize( RootNode rootNode, OutputStream outputStream ) throws Exception
+    {
+        startSerialize( rootNode, outputStream );
+        writeRootNode( rootNode );
+        endSerialize( rootNode, outputStream );
+    }
+
+    protected abstract void startWriteRootNode( RootNode rootNode ) throws Exception;
+
+    protected void writeRootNode( RootNode rootNode ) throws Exception
+    {
+        startWriteRootNode( rootNode );
+
+        for ( Node node : rootNode.getChildren() )
+        {
+            dispatcher( node );
+            flushStream();
+        }
+
+        endWriteRootNode( rootNode );
+        flushStream();
+    }
+
+    protected abstract void endWriteRootNode( RootNode rootNode ) throws Exception;
+
+    protected abstract void startWriteSimpleNode( SimpleNode simpleNode ) throws Exception;
+
+    protected void writeSimpleNode( SimpleNode simpleNode ) throws Exception
+    {
+        startWriteSimpleNode( simpleNode );
+        endWriteSimpleNode( simpleNode );
+    }
+
+    protected abstract void endWriteSimpleNode( SimpleNode simpleNode ) throws Exception;
+
+    protected abstract void startWriteComplexNode( ComplexNode complexNode ) throws Exception;
+
+    protected void writeComplexNode( ComplexNode complexNode ) throws Exception
+    {
+        startWriteComplexNode( complexNode );
+
+        for ( Node node : complexNode.getChildren() )
+        {
+            dispatcher( node );
+        }
+
+        endWriteComplexNode( complexNode );
+    }
+
+    protected abstract void endWriteComplexNode( ComplexNode complexNode ) throws Exception;
+
+    protected abstract void startWriteCollectionNode( CollectionNode collectionNode ) throws Exception;
+
+    protected void writeCollectionNode( CollectionNode collectionNode ) throws Exception
+    {
+        startWriteCollectionNode( collectionNode );
+
+        for ( Node node : collectionNode.getChildren() )
+        {
+            dispatcher( node );
+        }
+
+        endWriteCollectionNode( collectionNode );
+    }
+
+    protected abstract void endWriteCollectionNode( CollectionNode collectionNode ) throws Exception;
+
+    private void dispatcher( Node node ) throws Exception
+    {
+        switch ( node.getType() )
+        {
+            case SIMPLE:
+                writeSimpleNode( (SimpleNode) node );
+                break;
+            case COMPLEX:
+                writeComplexNode( (ComplexNode) node );
+                break;
+            case COLLECTION:
+                writeCollectionNode( (CollectionNode) node );
+                break;
+        }
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Deserializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Deserializer.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Deserializer.java	2014-06-08 10:58:50 +0000
@@ -0,0 +1,42 @@
+package org.hisp.dhis.node;
+
+/*
+ * Copyright (c) 2004-2014, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ */
+
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public interface Deserializer<T>
+{
+    List<String> contentTypes();
+
+    T deserialize( InputStream inputStream ) throws Exception;
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Node.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Node.java	2014-06-06 07:40:49 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Node.java	2014-06-08 10:58:50 +0000
@@ -53,6 +53,12 @@
     NodeType getType();
 
     /**
+     * Get parent node, or null if this is a top-level node.
+     * @return parent or null if node does not have parent
+     */
+    Node getParent();
+
+    /**
      * @param type Type to check for
      * @return True if node is of this type
      */

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeDeserializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeDeserializer.java	2014-06-04 20:15:48 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeDeserializer.java	2014-06-08 10:58:50 +0000
@@ -30,16 +30,15 @@
 
 import org.hisp.dhis.node.types.RootNode;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
 /**
  * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
  */
-public interface NodeDeserializer
+public interface NodeDeserializer extends Deserializer<RootNode>
 {
     List<String> contentTypes();
 
-    RootNode deserialize( InputStream inputStream ) throws IOException;
+    RootNode deserialize( InputStream inputStream ) throws Exception;
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeSerializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeSerializer.java	2014-06-04 16:22:56 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeSerializer.java	2014-06-08 10:58:50 +0000
@@ -30,16 +30,15 @@
 
 import org.hisp.dhis.node.types.RootNode;
 
-import java.io.IOException;
 import java.io.OutputStream;
 import java.util.List;
 
 /**
  * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
  */
-public interface NodeSerializer
+public interface NodeSerializer extends Serializer<RootNode>
 {
     List<String> contentTypes();
 
-    void serialize( RootNode rootNode, OutputStream outputStream ) throws IOException;
+    void serialize( RootNode rootNode, OutputStream outputStream ) throws Exception;
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeService.java	2014-06-05 11:56:44 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/NodeService.java	2014-06-08 10:58:50 +0000
@@ -30,7 +30,6 @@
 
 import org.hisp.dhis.node.types.RootNode;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
@@ -55,7 +54,7 @@
      * @param contentType  NodeSerializer contentType
      * @param outputStream Write to this outputStream
      */
-    void serialize( RootNode rootNode, String contentType, OutputStream outputStream ) throws IOException;
+    void serialize( RootNode rootNode, String contentType, OutputStream outputStream );
 
     /**
      * Find a nodeDeserializer that supports contentType or return null.
@@ -71,5 +70,5 @@
      * @param inputStream Read RootNode from this stream
      * @return RootNode deserialized from inputStream
      */
-    RootNode deserialize( String contentType, InputStream inputStream ) throws IOException;
+    RootNode deserialize( String contentType, InputStream inputStream );
 }

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Serializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Serializer.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/Serializer.java	2014-06-08 10:58:50 +0000
@@ -0,0 +1,42 @@
+package org.hisp.dhis.node;
+
+/*
+ * Copyright (c) 2004-2014, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ */
+
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public interface Serializer<T>
+{
+    List<String> contentTypes();
+
+    void serialize( T object, OutputStream outputStream ) throws Exception;
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/Jackson2JsonNodeSerializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/Jackson2JsonNodeSerializer.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/Jackson2JsonNodeSerializer.java	2014-06-08 10:58:50 +0000
@@ -0,0 +1,161 @@
+package org.hisp.dhis.node.serializers;
+
+/*
+ * Copyright (c) 2004-2014, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ */
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.google.common.collect.Lists;
+import org.hisp.dhis.node.AbstractNodeSerializer;
+import org.hisp.dhis.node.types.CollectionNode;
+import org.hisp.dhis.node.types.ComplexNode;
+import org.hisp.dhis.node.types.RootNode;
+import org.hisp.dhis.node.types.SimpleNode;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
+import org.springframework.stereotype.Component;
+
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+@Component
+@Scope( value = "prototype", proxyMode = ScopedProxyMode.INTERFACES )
+public class Jackson2JsonNodeSerializer extends AbstractNodeSerializer
+{
+    public static final String CONTENT_TYPE = "application/json";
+
+    private final static ObjectMapper objectMapper = new ObjectMapper();
+
+    static
+    {
+        objectMapper.setSerializationInclusion( JsonInclude.Include.NON_NULL );
+        objectMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
+        objectMapper.configure( SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false );
+        objectMapper.configure( SerializationFeature.WRAP_EXCEPTIONS, true );
+        objectMapper.getFactory().enable( JsonGenerator.Feature.QUOTE_FIELD_NAMES );
+    }
+
+    private JsonGenerator generator;
+
+    @Override
+    public List<String> contentTypes()
+    {
+        return Lists.newArrayList( CONTENT_TYPE );
+    }
+
+    @Override
+    protected void flushStream() throws Exception
+    {
+        generator.flush();
+    }
+
+    @Override
+    protected boolean startSerialize( RootNode rootNode, OutputStream outputStream ) throws Exception
+    {
+        generator = objectMapper.getFactory().createGenerator( outputStream );
+        return true;
+    }
+
+    @Override
+    protected void startWriteRootNode( RootNode rootNode ) throws Exception
+    {
+        generator.writeStartObject();
+    }
+
+    @Override
+    protected void endWriteRootNode( RootNode rootNode ) throws Exception
+    {
+        generator.writeEndObject();
+    }
+
+    @Override
+    protected void startWriteSimpleNode( SimpleNode simpleNode ) throws Exception
+    {
+        if ( simpleNode.getValue() == null ) // add hint for this, exclude if null
+        {
+            return;
+        }
+
+        if ( simpleNode.getParent().isCollection() )
+        {
+            generator.writeObject( simpleNode.getValue() );
+        }
+        else
+        {
+            generator.writeObjectField( simpleNode.getName(), simpleNode.getValue() );
+        }
+    }
+
+    @Override
+    protected void endWriteSimpleNode( SimpleNode simpleNode ) throws Exception
+    {
+    }
+
+    @Override
+    protected void startWriteComplexNode( ComplexNode complexNode ) throws Exception
+    {
+        if ( complexNode.getParent().isCollection() )
+        {
+            generator.writeStartObject();
+        }
+        else
+        {
+            generator.writeObjectFieldStart( complexNode.getName() );
+        }
+    }
+
+    @Override
+    protected void endWriteComplexNode( ComplexNode complexNode ) throws Exception
+    {
+        generator.writeEndObject();
+    }
+
+    @Override
+    protected void startWriteCollectionNode( CollectionNode collectionNode ) throws Exception
+    {
+        if ( collectionNode.getParent().isCollection() )
+        {
+            generator.writeStartArray();
+        }
+        else
+        {
+            generator.writeArrayFieldStart( collectionNode.getName() );
+        }
+    }
+
+    @Override
+    protected void endWriteCollectionNode( CollectionNode collectionNode ) throws Exception
+    {
+        generator.writeEndArray();
+    }
+}

=== removed file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/JacksonJsonNodeSerializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/JacksonJsonNodeSerializer.java	2014-06-04 16:22:56 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/JacksonJsonNodeSerializer.java	1970-01-01 00:00:00 +0000
@@ -1,165 +0,0 @@
-package org.hisp.dhis.node.serializers;
-
-/*
- * Copyright (c) 2004-2014, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
- */
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.google.common.collect.Lists;
-import org.hisp.dhis.node.Node;
-import org.hisp.dhis.node.NodeSerializer;
-import org.hisp.dhis.node.types.CollectionNode;
-import org.hisp.dhis.node.types.ComplexNode;
-import org.hisp.dhis.node.types.RootNode;
-import org.hisp.dhis.node.types.SimpleNode;
-import org.springframework.stereotype.Component;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.List;
-
-/**
- * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
- */
-@Component
-public class JacksonJsonNodeSerializer implements NodeSerializer
-{
-    public static final String CONTENT_TYPE = "application/json";
-
-    private final ObjectMapper objectMapper = new ObjectMapper();
-
-    @Override
-    public List<String> contentTypes()
-    {
-        return Lists.newArrayList( CONTENT_TYPE );
-    }
-
-    public JacksonJsonNodeSerializer()
-    {
-        objectMapper.setSerializationInclusion( JsonInclude.Include.NON_NULL );
-        objectMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
-        objectMapper.configure( SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false );
-        objectMapper.configure( SerializationFeature.WRAP_EXCEPTIONS, true );
-        objectMapper.getFactory().enable( JsonGenerator.Feature.QUOTE_FIELD_NAMES );
-    }
-
-    @Override
-    public void serialize( RootNode rootNode, OutputStream outputStream ) throws IOException
-    {
-        JsonGenerator generator = objectMapper.getFactory().createGenerator( outputStream );
-
-        writeRootNode( rootNode, generator );
-        generator.flush();
-    }
-
-    private void writeRootNode( RootNode rootNode, JsonGenerator generator ) throws IOException
-    {
-        generator.writeStartObject();
-
-        for ( Node node : rootNode.getChildren() )
-        {
-            dispatcher( node, generator, true );
-            generator.flush();
-        }
-
-        generator.writeEndObject();
-    }
-
-    private void writeSimpleNode( SimpleNode simpleNode, JsonGenerator generator, boolean writeKey ) throws IOException
-    {
-        if ( simpleNode.getValue() == null ) // add hint for this, exclude if null
-        {
-            return;
-        }
-
-        if ( writeKey )
-        {
-            generator.writeObjectField( simpleNode.getName(), simpleNode.getValue() );
-        }
-        else
-        {
-            generator.writeObject( simpleNode.getValue() );
-        }
-    }
-
-    private void writeComplexNode( ComplexNode complexNode, JsonGenerator generator, boolean writeKey ) throws IOException
-    {
-        if ( writeKey )
-        {
-            generator.writeObjectFieldStart( complexNode.getName() );
-        }
-        else
-        {
-            generator.writeStartObject();
-        }
-
-        for ( Node node : complexNode.getChildren() )
-        {
-            dispatcher( node, generator, true );
-        }
-
-        generator.writeEndObject();
-    }
-
-    private void writeCollectionNode( CollectionNode collectionNode, JsonGenerator generator, boolean writeKey ) throws IOException
-    {
-        if ( writeKey )
-        {
-            generator.writeArrayFieldStart( collectionNode.getName() );
-        }
-        else
-        {
-            generator.writeStartArray();
-        }
-
-        for ( Node node : collectionNode.getChildren() )
-        {
-            dispatcher( node, generator, false );
-        }
-
-        generator.writeEndArray();
-    }
-
-    private void dispatcher( Node node, JsonGenerator generator, boolean writeKey ) throws IOException
-    {
-        switch ( node.getType() )
-        {
-            case SIMPLE:
-                writeSimpleNode( (SimpleNode) node, generator, writeKey );
-                break;
-            case COMPLEX:
-                writeComplexNode( (ComplexNode) node, generator, writeKey );
-                break;
-            case COLLECTION:
-                writeCollectionNode( (CollectionNode) node, generator, writeKey );
-                break;
-        }
-    }
-}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/StAXNodeSerializer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/StAXNodeSerializer.java	2014-06-06 07:40:49 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/StAXNodeSerializer.java	2014-06-08 10:58:50 +0000
@@ -29,21 +29,20 @@
  */
 
 import com.google.common.collect.Lists;
+import org.hisp.dhis.node.AbstractNodeSerializer;
 import org.hisp.dhis.node.Node;
-import org.hisp.dhis.node.NodeSerializer;
 import org.hisp.dhis.node.types.CollectionNode;
 import org.hisp.dhis.node.types.ComplexNode;
 import org.hisp.dhis.node.types.RootNode;
 import org.hisp.dhis.node.types.SimpleNode;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
-import java.io.IOException;
 import java.io.OutputStream;
 import java.util.List;
 
@@ -51,12 +50,19 @@
  * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
  */
 @Component
-@Scope( ConfigurableBeanFactory.SCOPE_PROTOTYPE )
-public class StAXNodeSerializer implements NodeSerializer
+@Scope( value = "prototype", proxyMode = ScopedProxyMode.INTERFACES )
+public class StAXNodeSerializer extends AbstractNodeSerializer
 {
     public static final String CONTENT_TYPE = "application/xml";
 
-    private final XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
+    private static final XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
+
+    private XMLStreamWriter writer;
+
+    static
+    {
+        xmlFactory.setProperty( "javax.xml.stream.isRepairingNamespaces", true );
+    }
 
     @Override
     public List<String> contentTypes()
@@ -65,25 +71,21 @@
     }
 
     @Override
-    public void serialize( RootNode rootNode, OutputStream outputStream ) throws IOException
-    {
-        XMLStreamWriter writer;
-
-        try
-        {
-            xmlFactory.setProperty( "javax.xml.stream.isRepairingNamespaces", true );
-            writer = xmlFactory.createXMLStreamWriter( outputStream );
-            writer.setDefaultNamespace( rootNode.getDefaultNamespace() );
-            writeRootNode( rootNode, writer );
-            writer.flush();
-        }
-        catch ( XMLStreamException e )
-        {
-            throw new IOException( e.getMessage(), e.getCause() );
-        }
-    }
-
-    private void writeRootNode( RootNode rootNode, XMLStreamWriter writer ) throws IOException, XMLStreamException
+    protected boolean startSerialize( RootNode rootNode, OutputStream outputStream ) throws Exception
+    {
+        writer = xmlFactory.createXMLStreamWriter( outputStream );
+        writer.setDefaultNamespace( rootNode.getDefaultNamespace() );
+        return true;
+    }
+
+    @Override
+    protected void flushStream() throws Exception
+    {
+        writer.flush();
+    }
+
+    @Override
+    protected void startWriteRootNode( RootNode rootNode ) throws Exception
     {
         writer.writeStartDocument( "UTF-8", "1.0" );
 
@@ -92,19 +94,18 @@
             writer.writeComment( rootNode.getComment() );
         }
 
-        writeStartElement( rootNode, writer );
-
-        for ( Node node : rootNode.getChildren() )
-        {
-            dispatcher( node, writer );
-            writer.flush();
-        }
-
-        writeEndElement( writer );
+        writeStartElement( rootNode );
+    }
+
+    @Override
+    protected void endWriteRootNode( RootNode rootNode ) throws Exception
+    {
+        writer.writeEndElement();
         writer.writeEndDocument();
     }
 
-    private void writeSimpleNode( SimpleNode simpleNode, XMLStreamWriter writer ) throws XMLStreamException
+    @Override
+    protected void startWriteSimpleNode( SimpleNode simpleNode ) throws Exception
     {
         if ( simpleNode.getValue() == null ) // TODO include null or not?
         {
@@ -117,7 +118,7 @@
         {
             if ( !StringUtils.isEmpty( simpleNode.getNamespace() ) )
             {
-                writer.writeAttribute( "", simpleNode.getNamespace(), simpleNode.getName(), value );
+                writer.writeAttribute( simpleNode.getNamespace(), simpleNode.getName(), value );
             }
             else
             {
@@ -126,65 +127,57 @@
         }
         else
         {
-            writeStartElement( simpleNode, writer );
+            writeStartElement( simpleNode );
             writer.writeCharacters( value );
-            writeEndElement( writer );
-        }
-    }
-
-    private void writeComplexNode( ComplexNode complexNode, XMLStreamWriter writer ) throws XMLStreamException, IOException
-    {
-        writeStartElement( complexNode, writer );
-
-        for ( Node node : complexNode.getChildren() )
-        {
-            dispatcher( node, writer );
-        }
-
-        writeEndElement( writer );
-    }
-
-    private void writeCollectionNode( CollectionNode collectionNode, XMLStreamWriter writer ) throws XMLStreamException, IOException
-    {
-        if ( collectionNode.isWrapping() )
-        {
-            writeStartElement( collectionNode, writer );
-        }
-
-        for ( Node node : collectionNode.getChildren() )
-        {
-            dispatcher( node, writer );
-        }
-
-        if ( collectionNode.isWrapping() )
-        {
-            writeEndElement( writer );
-        }
-    }
-
-    private void dispatcher( Node node, XMLStreamWriter writer ) throws IOException, XMLStreamException
+        }
+    }
+
+    @Override
+    protected void endWriteSimpleNode( SimpleNode simpleNode ) throws Exception
+    {
+        if ( !simpleNode.isAttribute() && simpleNode.getValue() != null )
+        {
+            writer.writeEndElement();
+        }
+    }
+
+    @Override
+    protected void startWriteComplexNode( ComplexNode complexNode ) throws Exception
+    {
+        writeStartElement( complexNode );
+    }
+
+    @Override
+    protected void endWriteComplexNode( ComplexNode complexNode ) throws Exception
+    {
+        writer.writeEndElement();
+    }
+
+    @Override
+    protected void startWriteCollectionNode( CollectionNode collectionNode ) throws Exception
+    {
+        if ( collectionNode.isWrapping() )
+        {
+            writeStartElement( collectionNode );
+        }
+    }
+
+    @Override
+    protected void endWriteCollectionNode( CollectionNode collectionNode ) throws Exception
+    {
+        if ( collectionNode.isWrapping() )
+        {
+            writer.writeEndElement();
+        }
+    }
+
+    private void writeStartElement( Node node ) throws XMLStreamException
     {
         if ( !StringUtils.isEmpty( node.getComment() ) )
         {
             writer.writeComment( node.getComment() );
         }
 
-        switch ( node.getType() )
-        {
-            case SIMPLE:
-                writeSimpleNode( (SimpleNode) node, writer );
-                break;
-            case COMPLEX:
-                writeComplexNode( (ComplexNode) node, writer );
-                break;
-            case COLLECTION:
-                writeCollectionNode( (CollectionNode) node, writer );
-                break;
-        }
-    }
-
-    private void writeStartElement( Node node, XMLStreamWriter writer ) throws XMLStreamException
-    {
         if ( !StringUtils.isEmpty( node.getNamespace() ) )
         {
             writer.writeStartElement( node.getNamespace(), node.getName() );
@@ -194,9 +187,4 @@
             writer.writeStartElement( node.getName() );
         }
     }
-
-    private void writeEndElement( XMLStreamWriter writer ) throws XMLStreamException
-    {
-        writer.writeEndElement();
-    }
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/types/SimpleNode.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/types/SimpleNode.java	2014-06-06 07:40:49 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/types/SimpleNode.java	2014-06-08 10:58:50 +0000
@@ -28,14 +28,11 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
  */
 
-import com.google.common.collect.Lists;
 import org.hisp.dhis.node.AbstractNode;
 import org.hisp.dhis.node.Node;
 import org.hisp.dhis.node.NodeType;
 import org.hisp.dhis.node.exception.InvalidTypeException;
 
-import java.util.List;
-
 /**
  * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
  */
@@ -52,12 +49,6 @@
         this.attribute = false;
     }
 
-    public SimpleNode( String name, Object value, boolean attribute )
-    {
-        this( name, value );
-        this.attribute = attribute;
-    }
-
     public Object getValue()
     {
         return value;

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/node/DefaultNodeService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/node/DefaultNodeService.java	2014-06-06 07:40:49 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/node/DefaultNodeService.java	2014-06-08 10:58:50 +0000
@@ -34,7 +34,6 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import javax.annotation.PostConstruct;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
@@ -45,10 +44,10 @@
  */
 public class DefaultNodeService implements NodeService
 {
-    @Autowired( required = false )
+    @Autowired(required = false)
     private List<NodeSerializer> nodeSerializers = Lists.newArrayList();
 
-    @Autowired( required = false )
+    @Autowired(required = false)
     private List<NodeDeserializer> nodeDeserializers = Lists.newArrayList();
 
     private Map<String, NodeSerializer> nodeSerializerMap = Maps.newHashMap();
@@ -87,7 +86,7 @@
     }
 
     @Override
-    public void serialize( RootNode rootNode, String contentType, OutputStream outputStream ) throws IOException
+    public void serialize( RootNode rootNode, String contentType, OutputStream outputStream )
     {
         NodeSerializer nodeSerializer = getNodeSerializer( contentType );
 
@@ -96,7 +95,14 @@
             return; // TODO throw exception?
         }
 
-        nodeSerializer.serialize( rootNode, outputStream );
+        try
+        {
+            nodeSerializer.serialize( rootNode, outputStream );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
     }
 
     @Override
@@ -111,7 +117,7 @@
     }
 
     @Override
-    public RootNode deserialize( String contentType, InputStream inputStream ) throws IOException
+    public RootNode deserialize( String contentType, InputStream inputStream )
     {
         NodeDeserializer nodeDeserializer = getNodeDeserializer( contentType );
 
@@ -120,6 +126,15 @@
             return null; // TODO throw exception?
         }
 
-        return nodeDeserializer.deserialize( inputStream );
+        try
+        {
+            return nodeDeserializer.deserialize( inputStream );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        return null;
     }
 }