diff --git a/README.md b/README.md index d0b63c9..18e0a1a 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,7 @@ Within the marshalling configuration closure there are several configuration opt * *serializer* is a configuration option which allows us to define closures with custom serialization behavior. This configuration options allows us to customize serialization output for existing property (json,xml) * *virtual* unlike *serializer* which will create completely new property * *ignore* is a comma separated list of properties which should be ignored during serialization process (json,xml) +* *include* is an exclusive comma separated list of properties which will be included, meaning properties not listed will not be included (except for those specified using the should* options, or in case of XML, those listed as *attribute* or *identifier*). The *include* and *ignore* options are mutually exclusive - if both are defined, include takes priority (json,xml) ###Named and marshaller specific configuration diff --git a/src/groovy/org/grails/plugins/marshallers/GenericDomainClassJSONMarshaller.groovy b/src/groovy/org/grails/plugins/marshallers/GenericDomainClassJSONMarshaller.groovy index 4df11ad..12c80d3 100644 --- a/src/groovy/org/grails/plugins/marshallers/GenericDomainClassJSONMarshaller.groovy +++ b/src/groovy/org/grails/plugins/marshallers/GenericDomainClassJSONMarshaller.groovy @@ -71,86 +71,16 @@ class GenericDomainClassJSONMarshaller implements ObjectMarshaller { GrailsDomainClassProperty[] properties = domainClass.getPersistentProperties() + boolean includeMode = false + if(mc.include?.size() > 0) + includeMode = true + for (GrailsDomainClassProperty property : properties) { - if(!mc.ignore?.contains(property.getName())){ - writer.key(property.getName()) - if(mc.serializer?.containsKey(property.getName())){ - mc.serializer[property.getName()].call(value,writer) - } - else{ - if (!property.isAssociation()) { - // Write non-relation property - Object val = beanWrapper.getPropertyValue(property.getName()) - json.convertAnother(val) - } - else { - Object referenceObject = beanWrapper.getPropertyValue(property.getName()) - if (mc.deep?.contains(property.getName())) { - if (referenceObject == null) { - writer.value(null) - } - else { - referenceObject = proxyHandler.unwrapIfProxy(referenceObject) - if (referenceObject instanceof SortedMap) { - referenceObject = new TreeMap((SortedMap) referenceObject) - } - else if (referenceObject instanceof SortedSet) { - referenceObject = new TreeSet((SortedSet) referenceObject) - } - else if (referenceObject instanceof Set) { - referenceObject = new HashSet((Set) referenceObject) - } - else if (referenceObject instanceof Map) { - referenceObject = new HashMap((Map) referenceObject) - } - else if (referenceObject instanceof Collection) { - referenceObject = new ArrayList((Collection) referenceObject) - } - json.convertAnother(referenceObject) - } - } - else { - if (referenceObject == null) { - json.value(null) - } - else { - GrailsDomainClass referencedDomainClass = property.getReferencedDomainClass() - - // Embedded are now always fully rendered - if (referencedDomainClass == null || property.isEmbedded() || GCU.isJdk5Enum(property.getType())) { - json.convertAnother(referenceObject) - } - else if (property.isOneToOne() || property.isManyToOne() || property.isEmbedded()) { - asShortObject(referenceObject, json, referencedDomainClass.getIdentifier(), referencedDomainClass) - } - else { - GrailsDomainClassProperty referencedIdProperty = referencedDomainClass.getIdentifier() - @SuppressWarnings("unused") - String refPropertyName = referencedDomainClass.getPropertyName() - if (referenceObject instanceof Collection) { - Collection o = (Collection) referenceObject - writer.array() - for (Object el : o) { - asShortObject(el, json, referencedIdProperty, referencedDomainClass) - } - writer.endArray() - } - else if (referenceObject instanceof Map) { - Map map = (Map) referenceObject - for (Map.Entry entry : map.entrySet()) { - String key = String.valueOf(entry.getKey()) - Object o = entry.getValue() - writer.object() - writer.key(key) - asShortObject(o, json, referencedIdProperty, referencedDomainClass) - writer.endObject() - } - } - } - } - } - } - } + if(includeMode && mc.include?.contains(property.getName())) { + serializeProperty(property, mc, beanWrapper, json, writer, value) + } + else if(!includeMode && !mc.ignore?.contains(property.getName())) { + serializeProperty(property, mc, beanWrapper, json, writer, value) } } @@ -161,6 +91,88 @@ class GenericDomainClassJSONMarshaller implements ObjectMarshaller { writer.endObject() } + + protected void serializeProperty(GrailsDomainClassProperty property, MarshallingConfig mc, + BeanWrapper beanWrapper, JSON json, JSONWriter writer, def value) { + writer.key(property.getName()) + if(mc.serializer?.containsKey(property.getName())){ + mc.serializer[property.getName()].call(value,writer) + } + else{ + if (!property.isAssociation()) { + // Write non-relation property + Object val = beanWrapper.getPropertyValue(property.getName()) + json.convertAnother(val) + } + else { + Object referenceObject = beanWrapper.getPropertyValue(property.getName()) + if (mc.deep?.contains(property.getName())) { + if (referenceObject == null) { + writer.value(null) + } + else { + referenceObject = proxyHandler.unwrapIfProxy(referenceObject) + if (referenceObject instanceof SortedMap) { + referenceObject = new TreeMap((SortedMap) referenceObject) + } + else if (referenceObject instanceof SortedSet) { + referenceObject = new TreeSet((SortedSet) referenceObject) + } + else if (referenceObject instanceof Set) { + referenceObject = new HashSet((Set) referenceObject) + } + else if (referenceObject instanceof Map) { + referenceObject = new HashMap((Map) referenceObject) + } + else if (referenceObject instanceof Collection) { + referenceObject = new ArrayList((Collection) referenceObject) + } + json.convertAnother(referenceObject) + } + } + else { + if (referenceObject == null) { + json.value(null) + } + else { + GrailsDomainClass referencedDomainClass = property.getReferencedDomainClass() + + // Embedded are now always fully rendered + if (referencedDomainClass == null || property.isEmbedded() || GCU.isJdk5Enum(property.getType())) { + json.convertAnother(referenceObject) + } + else if (property.isOneToOne() || property.isManyToOne() || property.isEmbedded()) { + asShortObject(referenceObject, json, referencedDomainClass.getIdentifier(), referencedDomainClass) + } + else { + GrailsDomainClassProperty referencedIdProperty = referencedDomainClass.getIdentifier() + @SuppressWarnings("unused") + String refPropertyName = referencedDomainClass.getPropertyName() + if (referenceObject instanceof Collection) { + Collection o = (Collection) referenceObject + writer.array() + for (Object el : o) { + asShortObject(el, json, referencedIdProperty, referencedDomainClass) + } + writer.endArray() + } + else if (referenceObject instanceof Map) { + Map map = (Map) referenceObject + for (Map.Entry entry : map.entrySet()) { + String key = String.valueOf(entry.getKey()) + Object o = entry.getValue() + writer.object() + writer.key(key) + asShortObject(o, json, referencedIdProperty, referencedDomainClass) + writer.endObject() + } + } + } + } + } + } + } + } protected void asShortObject(Object refObj, JSON json, GrailsDomainClassProperty idProperty, GrailsDomainClass referencedDomainClass) throws ConverterException { Object idValue diff --git a/src/groovy/org/grails/plugins/marshallers/GenericDomainClassXMLMarshaller.groovy b/src/groovy/org/grails/plugins/marshallers/GenericDomainClassXMLMarshaller.groovy index 4eead71..a0e5364 100644 --- a/src/groovy/org/grails/plugins/marshallers/GenericDomainClassXMLMarshaller.groovy +++ b/src/groovy/org/grails/plugins/marshallers/GenericDomainClassXMLMarshaller.groovy @@ -97,11 +97,16 @@ class GenericDomainClassXMLMarshaller implements ObjectMarshaller,NameAware } } + boolean includeMode = false + if(mc.include?.size() > 0) + includeMode = true GrailsDomainClassProperty[] properties = domainClass.getPersistentProperties() for (GrailsDomainClassProperty property : properties) { - if(!mc.identifier?.contains(property.getName()) && !mc.ignore?.contains(property.getName()) && !mc.attribute?.contains(property.getName())){ + if(!mc.identifier?.contains(property.getName()) && !mc.attribute?.contains(property.getName()) && + (!includeMode && !mc.ignore?.contains(property.getName()) + || includeMode && mc.include?.contains(property.getName())) ){ def serializers=mc?.serializer Object val = beanWrapper.getPropertyValue(property.getName()) if(serializers && serializers[property.name]){ diff --git a/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfig.groovy b/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfig.groovy index 85b8249..aeb929f 100644 --- a/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfig.groovy +++ b/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfig.groovy @@ -26,6 +26,10 @@ class MarshallingConfig { * list of field names which will be ignored during serialization */ List ignore + /** + * exclusive list of field names which will be included during serialization (mutually exclusive with "ignore") + */ + List include /** * configuration option allows us to define closures with custom serialization behavior */ diff --git a/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfigBuilder.groovy b/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfigBuilder.groovy index eb15fbe..f3fc4eb 100644 --- a/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfigBuilder.groovy +++ b/src/groovy/org/grails/plugins/marshallers/config/MarshallingConfigBuilder.groovy @@ -12,7 +12,7 @@ class MarshallingConfigBuilder { } MarshallingConfigBuilder(MarshallingConfig c){ - ['clazz', 'deep','identifier','elementName','attribute','virtual','shouldOutputIdentifier','shouldOutputClass','shouldOutputVersion','ignore'].each{p-> + ['clazz','deep','identifier','elementName','attribute','virtual','shouldOutputIdentifier','shouldOutputClass','shouldOutputVersion','ignore','include'].each{p-> if(c[p]!=null){ config[p]=c[p] } @@ -41,6 +41,9 @@ class MarshallingConfigBuilder { void ignore(String... args){ config.ignore=args as List } + void include(String... args){ + config.include=args as List + } void virtual(Closure arg){ VirtualPropertiesBuilder builder=new VirtualPropertiesBuilder() arg.delegate=builder diff --git a/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassJSONMarshallerUnitSpec.groovy b/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassJSONMarshallerUnitSpec.groovy index ea4e4b9..a350633 100644 --- a/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassJSONMarshallerUnitSpec.groovy +++ b/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassJSONMarshallerUnitSpec.groovy @@ -39,7 +39,7 @@ class GenericDomainClassJSONMarshallerUnitSpec extends Specification { invoice.save() } - def "setting shouldOutputIdentifier to false should supress output of identifier"(){ + def "setting shouldOutputIdentifier to false should suppress output of identifier"(){ given: Invoice.marshalling = { shouldOutputIdentifier false } initialize() @@ -51,7 +51,7 @@ class GenericDomainClassJSONMarshallerUnitSpec extends Specification { m.id==null } - def "putting a property to a list of ignores should supress property serialization"(){ + def "putting a property to a list of ignores should suppress property serialization"(){ given: Invoice.marshalling = { ignore 'admin','created' } initialize() @@ -64,6 +64,20 @@ class GenericDomainClassJSONMarshallerUnitSpec extends Specification { m.id!=null } + def "putting a property to a list of includes should suppress other properties serialization"(){ + given: + Invoice.marshalling = { include 'name' } + initialize() + when: + def j=new JSON(invoice) + def m=JSON.parse(j.toString()) + then: + m.name!=null + m.admin==null + m.created==null + m.id!=null + } + def "setting shouldOutputClass and shouldOutputVersion to true should output class and version info"(){ given: Invoice.marshalling = { @@ -134,9 +148,9 @@ class GenericDomainClassJSONMarshallerUnitSpec extends Specification { initialize() when: def j - JSON.use('named'){ + JSON.use('named',{ j=new JSON(invoice) - } + }) def m=JSON.parse(j.toString()) then: m.items.size()==2 @@ -159,11 +173,11 @@ class GenericDomainClassJSONMarshallerUnitSpec extends Specification { } initialize() when: - def j - JSON.use('named'){ + JSON j + JSON.use('named',{ j=new JSON(invoice) - } - def m=JSON.parse(j.toString()) + }) + def m=JSON.parse(j.toString()) then: m.items.size()==2 !m.items[0].name diff --git a/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassXMLMarshallerUnitSpec.groovy b/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassXMLMarshallerUnitSpec.groovy index 224ba00..f533780 100644 --- a/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassXMLMarshallerUnitSpec.groovy +++ b/test/unit/org/grails/plugins/marshallers/config/GenericDomainClassXMLMarshallerUnitSpec.groovy @@ -43,7 +43,7 @@ class GenericDomainClassXMLMarshallerUnitSpec extends Specification { invoice.save() } - def "setting shouldOutputIdentifier to false should supress output of identifier"(){ + def "setting shouldOutputIdentifier to false should suppress output of identifier"(){ given: Invoice.marshalling = { shouldOutputIdentifier false } initialize() @@ -55,7 +55,7 @@ class GenericDomainClassXMLMarshallerUnitSpec extends Specification { !m.id.text() } - def "putting a property to a list of ignores should supress property serialization"(){ + def "putting a property to a list of ignores should suppress property serialization"(){ given: Invoice.marshalling = { ignore 'admin','created' } initialize() @@ -67,6 +67,20 @@ class GenericDomainClassXMLMarshallerUnitSpec extends Specification { !m.created.text() m.@id.text() } + + def "putting a property to a list of includes should suppress other properties serialization"(){ + given: + Invoice.marshalling = { include 'name' } + initialize() + when: + def j=new XML(invoice) + def m=new XmlSlurper().parseText(j.toString()) + then: + m.name.text() + !m.admin.text() + !m.created.text() + m.@id.text() + } def "putting a property to a list of attributes output value as attribute"(){ given: @@ -196,9 +210,9 @@ class GenericDomainClassXMLMarshallerUnitSpec extends Specification { initialize() when: def j - XML.use('named'){ + XML.use('named',{ j=new XML(invoice) - } + }) def m=new XmlSlurper().parseText(j.toString()) then: m.items.item.size()==2