=== 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-01 14:30:40 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/AbstractNode.java 2014-06-02 22:47:30 +0000 @@ -69,6 +69,11 @@ @Override public T addNode( T node ) throws InvalidTypeException { + if ( node == null ) + { + return null; + } + nodes.add( node ); return node; } === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/exception/InvalidTypeException.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/exception/InvalidTypeException.java 2014-05-31 23:48:24 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/exception/InvalidTypeException.java 2014-06-02 22:47:30 +0000 @@ -31,7 +31,7 @@ /** * @author Morten Olav Hansen */ -public class InvalidTypeException extends Exception +public class InvalidTypeException extends RuntimeException { public InvalidTypeException( String message ) { === modified 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-01 12:21:14 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/JacksonJsonNodeSerializer.java 2014-06-02 22:47:30 +0000 @@ -28,8 +28,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE */ -import com.fasterxml.jackson.core.JsonFactory; +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 org.hisp.dhis.node.Node; import org.hisp.dhis.node.NodeSerializer; import org.hisp.dhis.node.types.CollectionNode; @@ -49,7 +51,7 @@ { public static final String CONTENT_TYPE = "application/json"; - private final JsonFactory jsonFactory = new JsonFactory(); + private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String contentType() @@ -59,13 +61,17 @@ 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 = jsonFactory.createGenerator( outputStream ); + JsonGenerator generator = objectMapper.getFactory().createGenerator( outputStream ); renderRootNode( rootNode, generator ); generator.flush(); @@ -86,6 +92,11 @@ private void renderSimpleNode( 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() ); === 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-01 12:21:14 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/serializers/StAXNodeSerializer.java 2014-06-02 22:47:30 +0000 @@ -94,6 +94,11 @@ private void renderSimpleNode( SimpleNode simpleNode, XMLStreamWriter writer ) throws XMLStreamException { + if ( simpleNode.getValue() == null ) // add hint for this, exclude if null + { + return; + } + String value = String.format( "%s", simpleNode.getValue() ); writeStartElement( simpleNode, writer ); @@ -103,6 +108,11 @@ private void renderSimpleNodeAttribute( SimpleNode simpleNode, XMLStreamWriter writer ) throws XMLStreamException { + if ( simpleNode.getValue() == null ) // add hint for this, exclude if null + { + return; + } + String value = String.format( "%s", simpleNode.getValue() ); if ( simpleNode.haveHint( NodeHint.Type.XML_NAMESPACE ) ) === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/DefaultSchemaService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/DefaultSchemaService.java 2014-06-02 21:00:27 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/DefaultSchemaService.java 2014-06-02 22:47:30 +0000 @@ -30,7 +30,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import javassist.util.proxy.ProxyFactory; +import org.hisp.dhis.system.util.ReflectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.OrderComparator; @@ -80,10 +80,7 @@ return null; } - if ( ProxyFactory.isProxyClass( klass ) ) - { - klass = klass.getSuperclass(); - } + klass = ReflectionUtils.getRealClass( klass ); if ( classSchemaMap.containsKey( klass ) ) { @@ -108,6 +105,8 @@ return schema; } + klass = ReflectionUtils.getRealClass( klass ); + schema = new Schema( klass, klass.getName(), klass.getName() ); schema.setPropertyMap( propertyIntrospectorService.getPropertiesMap( schema.getKlass() ) ); === modified file 'dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/filter/DefaultFilterService.java' --- dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/filter/DefaultFilterService.java 2014-06-02 21:35:16 +0000 +++ dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/filter/DefaultFilterService.java 2014-06-02 22:47:30 +0000 @@ -32,6 +32,9 @@ import com.google.common.collect.Maps; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.dxf2.filter.ops.Op; +import org.hisp.dhis.node.types.CollectionNode; +import org.hisp.dhis.node.types.ComplexNode; +import org.hisp.dhis.node.types.SimpleNode; import org.hisp.dhis.schema.Property; import org.hisp.dhis.schema.Schema; import org.hisp.dhis.schema.SchemaService; @@ -80,13 +83,15 @@ } @Override - public List filterProperties( List objects, String include, String exclude ) + public CollectionNode filterProperties( Class klass, List objects, + String include, String exclude ) { - List output = Lists.newArrayList(); + Schema rootSchema = schemaService.getDynamicSchema( klass ); + CollectionNode collectionNode = new CollectionNode( rootSchema.getPlural() ); // replace with 'xml' collection name if ( objects.isEmpty() ) { - return output; + return collectionNode; } Map fieldMap = Maps.newHashMap(); @@ -118,22 +123,22 @@ for ( Object object : objects ) { - output.add( buildObjectOutput( object, fieldMap ) ); + collectionNode.addNode( buildObjectOutput( fieldMap, object ) ); } - return output; + return collectionNode; } @SuppressWarnings( "unchecked" ) - private Map buildObjectOutput( Object object, Map fieldMap ) + private ComplexNode buildObjectOutput( Map fieldMap, Object object ) { if ( object == null ) { return null; } - Map output = Maps.newHashMap(); Schema schema = schemaService.getDynamicSchema( object.getClass() ); + ComplexNode complexNode = new ComplexNode( schema.getSingular() ); for ( String fieldKey : fieldMap.keySet() ) { @@ -156,58 +161,62 @@ { if ( !property.isIdentifiableObject() ) { - output.put( fieldKey, returnValue ); + complexNode.addNode( new SimpleNode( fieldKey, returnValue ) ); } else if ( !property.isCollection() ) { - output.put( fieldKey, getIdentifiableObjectProperties( returnValue ) ); + complexNode.addNode( getIdentifiableObjectProperties( returnValue, IDENTIFIABLE_PROPERTIES ) ); } else { - output.put( fieldKey, getIdentifiableObjectCollectionProperties( returnValue ) ); + complexNode.addNode( getIdentifiableObjectCollectionProperties( returnValue, IDENTIFIABLE_PROPERTIES, fieldKey ) ); } } else { if ( property.isCollection() ) { - List list = Lists.newArrayList(); - output.put( fieldKey, list ); + CollectionNode collectionNode = complexNode.addNode( new CollectionNode( property.getXmlCollectionName() ) ); - for ( Object obj : (Collection) returnValue ) + for ( Object collectionObject : (Collection) returnValue ) { - Map properties = buildObjectOutput( obj, fieldValue ); + ComplexNode node = buildObjectOutput( fieldValue, collectionObject ); - if ( !properties.isEmpty() ) + if ( !node.getNodes().isEmpty() ) { - list.add( properties ); + collectionNode.addNode( node ); } } } else { - Map map = buildObjectOutput( returnValue, fieldValue ); + ComplexNode node = buildObjectOutput( fieldValue, returnValue ); - if ( !map.isEmpty() ) + if ( !node.getNodes().isEmpty() ) { - output.put( fieldKey, map ); + complexNode.addNode( node ); } } } } - return output; - } - - private List> getIdentifiableObjectCollectionProperties( Object object ) - { - return getIdentifiableObjectCollectionProperties( object, IDENTIFIABLE_PROPERTIES ); + return complexNode; } @SuppressWarnings( "unchecked" ) - private List> getIdentifiableObjectCollectionProperties( Object object, List fields ) + private CollectionNode getIdentifiableObjectCollectionProperties( Object object, List fields, String collectionName ) { - List> output = Lists.newArrayList(); + if ( object == null ) + { + return null; + } + + if ( !Collection.class.isInstance( object ) ) + { + return null; + } + + CollectionNode collectionNode = new CollectionNode( collectionName ); Collection identifiableObjects; try @@ -217,28 +226,33 @@ catch ( ClassCastException ex ) { ex.printStackTrace(); - return output; + return collectionNode; } for ( IdentifiableObject identifiableObject : identifiableObjects ) { - Map properties = getIdentifiableObjectProperties( identifiableObject, fields ); - output.add( properties ); - } - - return output; - } - - private Map getIdentifiableObjectProperties( Object object ) - { - return getIdentifiableObjectProperties( object, IDENTIFIABLE_PROPERTIES ); - } - - private Map getIdentifiableObjectProperties( Object object, List fields ) - { - Map map = Maps.newLinkedHashMap(); + collectionNode.addNode( getIdentifiableObjectProperties( identifiableObject, fields ) ); + } + + return collectionNode; + } + + private ComplexNode getIdentifiableObjectProperties( Object object, List fields ) + { + if ( object == null ) + { + return null; + } + + if ( !IdentifiableObject.class.isInstance( object ) ) + { + return null; + } + Schema schema = schemaService.getDynamicSchema( object.getClass() ); + ComplexNode complexNode = new ComplexNode( schema.getSingular() ); + for ( String field : fields ) { Property property = schema.getPropertyMap().get( field ); @@ -252,11 +266,11 @@ if ( o != null ) { - map.put( field, o ); + complexNode.addNode( new SimpleNode( field, o ) ); } } - return map; + return complexNode; } @SuppressWarnings( "unchecked" ) @@ -268,7 +282,6 @@ { if ( !schema.getPropertyMap().containsKey( field ) ) { - System.err.println( "Skipping non-existent field: " + field ); continue; } === modified file 'dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/filter/FilterService.java' --- dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/filter/FilterService.java 2014-04-14 06:43:16 +0000 +++ dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/filter/FilterService.java 2014-06-02 22:47:30 +0000 @@ -29,6 +29,7 @@ */ import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.node.types.CollectionNode; import java.util.List; @@ -54,5 +55,6 @@ * @param exclude Exclusion filter * @return List of objects with only wanted properties */ - List filterProperties( List objects, String include, String exclude ); + CollectionNode filterProperties( Class klass, List objects, + String include, String exclude ); } === modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ReflectionUtils.java' --- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ReflectionUtils.java 2014-03-26 11:32:35 +0000 +++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ReflectionUtils.java 2014-06-02 22:47:30 +0000 @@ -28,6 +28,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import javassist.util.proxy.ProxyFactory; +import org.hibernate.collection.spi.PersistentCollection; import org.hisp.dhis.system.util.functional.Function1; import org.hisp.dhis.system.util.functional.Predicate; import org.springframework.util.StringUtils; @@ -497,4 +499,19 @@ throw new RuntimeException( "Unknown Collection type." ); } } + + public static Class getRealClass( Class klass ) + { + if ( ProxyFactory.isProxyClass( klass ) ) + { + klass = klass.getSuperclass(); + } + + while ( PersistentCollection.class.isAssignableFrom( klass ) ) + { + klass = klass.getSuperclass(); + } + + return klass; + } } === modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java' --- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java 2014-06-02 19:07:47 +0000 +++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java 2014-06-02 22:47:30 +0000 @@ -29,7 +29,6 @@ */ import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import org.hisp.dhis.acl.Access; import org.hisp.dhis.acl.AclService; import org.hisp.dhis.common.BaseIdentifiableObject; @@ -46,6 +45,10 @@ import org.hisp.dhis.hibernate.exception.DeleteAccessDeniedException; import org.hisp.dhis.hibernate.exception.UpdateAccessDeniedException; import org.hisp.dhis.importexport.ImportStrategy; +import org.hisp.dhis.node.NodeService; +import org.hisp.dhis.node.types.ComplexNode; +import org.hisp.dhis.node.types.RootNode; +import org.hisp.dhis.node.types.SimpleNode; import org.hisp.dhis.schema.Schema; import org.hisp.dhis.schema.SchemaService; import org.hisp.dhis.system.util.ReflectionUtils; @@ -106,6 +109,9 @@ @Autowired protected ImportService importService; + @Autowired + protected NodeService nodeService; + //-------------------------------------------------------------------------- // GET //-------------------------------------------------------------------------- @@ -204,24 +210,21 @@ // enable property filter if ( include != null || exclude != null ) { - List objects = filterService.filterProperties( entityList, include, exclude ); - Map output = Maps.newLinkedHashMap(); + RootNode rootNode = new RootNode( "metadata" ); if ( hasPaging ) { - output.put( "pager", metaData.getPager() ); - } - - if ( schema != null ) - { - output.put( schema.getPlural(), objects ); - } - else - { - output.put( "objects", objects ); - } - - renderService.toJson( response.getOutputStream(), output ); + ComplexNode pagerNode = rootNode.addNode( new ComplexNode( "pager" ) ); + pagerNode.addNode( new SimpleNode( "page", metaData.getPager().getPage() ) ); + pagerNode.addNode( new SimpleNode( "pageCount", metaData.getPager().getPageCount() ) ); + pagerNode.addNode( new SimpleNode( "total", metaData.getPager().getTotal() ) ); + pagerNode.addNode( new SimpleNode( "nextPage", metaData.getPager().getNextPage() ) ); + pagerNode.addNode( new SimpleNode( "prevPage", metaData.getPager().getPrevPage() ) ); + } + + rootNode.addNode( filterService.filterProperties( getEntityClass(), entityList, include, exclude ) ); + + nodeService.serialize( rootNode, "application/json", response.getOutputStream() ); } else {