Skip to content

Commit

Permalink
Added a new include option (allow only specific properties - issue pe…
Browse files Browse the repository at this point in the history
  • Loading branch information
immutability authored and dhalupa committed Mar 14, 2015
1 parent f2048d6 commit ad618f1
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 93 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,86 +71,16 @@ class GenericDomainClassJSONMarshaller implements ObjectMarshaller<JSON> {

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<Object, Object> map = (Map<Object, Object>) referenceObject
for (Map.Entry<Object, Object> 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)
}
}

Expand All @@ -161,6 +91,88 @@ class GenericDomainClassJSONMarshaller implements ObjectMarshaller<JSON> {

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<Object, Object> map = (Map<Object, Object>) referenceObject
for (Map.Entry<Object, Object> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,16 @@ class GenericDomainClassXMLMarshaller implements ObjectMarshaller<XML>,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]){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -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 = {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ad618f1

Please sign in to comment.