Skip to content

Commit

Permalink
Added a new include option for JSON (allow only specific properties -…
Browse files Browse the repository at this point in the history
… issue pedjak#12)
  • Loading branch information
immutability committed Mar 3, 2015
1 parent c9c8d6c commit 47ee558
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 80 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). The *include* and *ignore* options are mutually exclusive - if both are defined, include takes priority (json)


###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 @@ -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 @@ -64,6 +64,20 @@ class GenericDomainClassJSONMarshallerUnitSpec extends Specification {
m.id!=null
}

def "putting a property to a list of includes should supress 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

0 comments on commit 47ee558

Please sign in to comment.