Skip to content

Commit 1c1587e

Browse files
author
graeme
committed
fix for GRAILS-1702
git-svn-id: https://svn.codehaus.org/grails/trunk@5782 1cfb16fd-6d17-0410-8ff1-b7e8e1e2867d
1 parent bfabaf9 commit 1c1587e

File tree

9 files changed

+1089
-560
lines changed

9 files changed

+1089
-560
lines changed

src/groovy/org/codehaus/groovy/grails/plugins/converters/ConvertersGrailsPlugin.groovy

Lines changed: 78 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
*/
1616
package org.codehaus.groovy.grails.plugins.converters
1717

18-
import grails.converters.*
19-
import org.codehaus.groovy.grails.commons.ControllerArtefactHandler
18+
import grails.converters.JSON
19+
import grails.converters.XML
2020
import grails.util.GrailsUtil
21-
import org.codehaus.groovy.grails.plugins.converters.codecs.JSONCodec
21+
import javax.servlet.http.HttpServletRequest
2222
import org.codehaus.groovy.grails.web.converters.Converter
2323
import org.codehaus.groovy.grails.web.converters.ConverterUtil
24+
import org.codehaus.groovy.grails.web.json.JSONArray
25+
import org.codehaus.groovy.grails.web.json.JSONObject
2426

2527
/**
2628
* A plug-in that allows the obj as XML syntax
@@ -33,74 +35,100 @@ import org.codehaus.groovy.grails.web.converters.ConverterUtil
3335
*
3436
*/
3537
class ConvertersGrailsPlugin {
36-
def version = GrailsUtil.getGrailsVersion()
38+
def version = GrailsUtil.getGrailsVersion()
3739

38-
def author = "Siegfried Puchbauer"
39-
def title = "Provides JSON and XML Conversion for common Objects (Domain Classes, Lists, Maps, POJO)"
40-
def description = """
40+
def author = "Siegfried Puchbauer"
41+
def title = "Provides JSON and XML Conversion for common Objects (Domain Classes, Lists, Maps, POJO)"
42+
def description = """
4143
The grails-converters plugin aims to give you the ability to convert your domain objects, maps and lists to JSON or XML very quickly, to ease development for AJAX based applications. The plugin leverages the the groovy "as" operator and extends the render method in grails controllers to directly send the result to the output stream. It also adds the Grails Codecs mechanism for XML and JSON.
4244
"""
43-
def documentation = "http://grails.org/Converters+Plugin"
44-
def providedArtefacts = [ JSONCodec]
45-
def observe = ["controllers"]
45+
def documentation = "http://grails.org/Converters+Plugin"
46+
def providedArtefacts = [org.codehaus.groovy.grails.plugins.converters.codecs.JSONCodec, org.codehaus.groovy.grails.plugins.converters.codecs.XMLCodec]
47+
def observe = ["controllers"]
4648

47-
def dependsOn = [
48-
controllers: GrailsUtil.getGrailsVersion(),
49-
domainClass: GrailsUtil.getGrailsVersion()
50-
]
49+
def dependsOn = [
50+
controllers: GrailsUtil.getGrailsVersion(),
51+
domainClass: GrailsUtil.getGrailsVersion()
52+
]
5153

52-
def renderMethod = { Converter converter ->
53-
converter.render(delegate.response);
54+
def renderMethod = {Converter converter ->
55+
converter.render(delegate.response);
5456
}
55-
def headerMethod = { String key, def value ->
56-
if(value) delegate.response?.setHeader(key, value.toString())
57+
def headerMethod = {String key, def value ->
58+
if (value) delegate.response?.setHeader(key, value.toString())
5759
}
58-
def jsonHeaderMethod = { def value ->
59-
if(value) delegate.response?.setHeader("X-JSON", value.toString())
60+
def jsonHeaderMethod = {def value ->
61+
def json = (value instanceof JSON || value instanceof JSONObject || value instanceof JSONArray) ? value : (new JSON(value));
62+
if (value) delegate.response?.setHeader("X-JSON", value.toString())
6063
}
6164

62-
def onChange = { event ->
65+
def onChange = {event ->
6366
def mc = event.source.metaClass
6467
mc.render = renderMethod
6568
mc.header = headerMethod
6669
mc.jsonHeader = jsonHeaderMethod
6770
}
6871

69-
def doWithDynamicMethods = { applicationContext ->
70-
ConverterUtil.setGrailsApplication(application);
72+
def doWithDynamicMethods = {applicationContext ->
73+
try {
74+
ConverterUtil.setGrailsApplication(application);
7175

72-
def controllerClasses = application.controllerClasses
73-
for(controller in controllerClasses) {
74-
def mc = controller.metaClass
75-
mc.render = renderMethod
76-
mc.header = headerMethod
77-
mc.jsonHeader = jsonHeaderMethod
78-
}
76+
log.debug "Applying new header and render methods to all Controllers..."
77+
def controllerClasses = application.controllerClasses
78+
for (controller in controllerClasses) {
79+
def mc = controller.metaClass
80+
mc.render = renderMethod
81+
mc.header = headerMethod
82+
mc.jsonHeader = jsonHeaderMethod
83+
}
7984

80-
def asTypeMethod = { Class clazz ->
81-
if(ConverterUtil.isConverterClass(clazz)) {
85+
def asTypeMethod = {java.lang.Class clazz ->
86+
if (ConverterUtil.isConverterClass(clazz)) {
8287
return ConverterUtil.createConverter(clazz, delegate)
8388
} else {
8489
return ConverterUtil.invokeOriginalAsTypeMethod(delegate, clazz)
8590
}
86-
}
91+
}
8792

88-
for(dc in application.domainClasses) {
89-
def mc = dc.metaClass
90-
mc.asType = asTypeMethod
91-
XML.addAlias(dc.propertyName, dc.clazz);
92-
log.debug "Adding XStream alias ${dc.propertyName} for class ${dc.clazz.getName()}"
93-
}
93+
for (dc in application.domainClasses) {
94+
def mc = dc.metaClass
95+
mc.asType = asTypeMethod
96+
ConverterUtil.addAlias(dc.propertyName, dc.clazz);
97+
log.debug "Adding XStream alias ${dc.propertyName} for class ${dc.clazz.getName()}"
98+
}
99+
100+
101+
// Override GDK asType for some common Interfaces and Classes
102+
[java.util.ArrayList, java.util.TreeSet, java.util.HashSet, java.util.List, java.util.Set, java.util.Collection, groovy.lang.GroovyObject, java.lang.Object].each {Class clazz ->
103+
def mc = GroovySystem.metaClassRegistry.getMetaClass(clazz)
104+
if (!mc instanceof ExpandoMetaClass) {
105+
log.warn "Unable to add Converter Functionality to Class ${className}"
106+
return;
107+
}
108+
log.debug "Adding Converter asType Method to Class ${clazz} [${clazz.class}] -> [${mc.class}]"
109+
mc.asType = asTypeMethod
110+
mc.initialize()
111+
}
94112

95-
[Collection,List,Set,ArrayList,HashSet,TreeSet, Object].each { collectionClass ->
96-
collectionClass.metaClass.asType = { Class clazz ->
97-
if(ConverterUtil.isConverterClass(clazz)) {
98-
return ConverterUtil.createConverter(clazz, delegate);
99-
} else {
100-
return ConverterUtil.invokeOriginalAsTypeMethod(delegate, clazz)
101-
}
102-
}
103-
}
104-
log.debug "Converters Plugin configured successfully"
105-
}
113+
// Methods for Reading JSON/XML from Requests
114+
def getXMLMethod = {->
115+
return XML.parse((HttpServletRequest) delegate)
116+
}
117+
def getJSONMethod = {->
118+
return JSON.parse((HttpServletRequest) delegate)
119+
}
120+
def requestMc = GroovySystem.metaClassRegistry.getMetaClass(HttpServletRequest)
121+
requestMc.getXML = getXMLMethod
122+
requestMc.getJSON = getJSONMethod
123+
requestMc.initialize()
124+
125+
// TODO:
126+
// add asType Method to XmlSlurper to unmarshalling
127+
// of XML Content and implement unmarshalling in JSONObject/Array...
128+
129+
log.debug "Converters Plugin configured successfully"
130+
} catch (Exception e) {
131+
log.error(e)
132+
}
133+
}
106134
}

src/web/grails/converters/JSON.java

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import groovy.lang.GString;
1818
import groovy.lang.GroovyObject;
19+
import org.apache.commons.io.IOUtils;
1920
import org.apache.commons.logging.Log;
2021
import org.apache.commons.logging.LogFactory;
2122
import org.codehaus.groovy.grails.commons.GrailsDomainClass;
@@ -24,17 +25,17 @@
2425
import org.codehaus.groovy.grails.web.converters.Converter;
2526
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
2627
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
27-
import org.codehaus.groovy.grails.web.json.JSONException;
28-
import org.codehaus.groovy.grails.web.json.JSONObject;
29-
import org.codehaus.groovy.grails.web.json.JSONWriter;
28+
import org.codehaus.groovy.grails.web.json.*;
3029
import org.springframework.beans.BeanWrapper;
3130
import org.springframework.beans.BeanWrapperImpl;
3231

32+
import javax.servlet.http.HttpServletRequest;
3333
import javax.servlet.http.HttpServletResponse;
3434
import java.beans.BeanInfo;
3535
import java.beans.Introspector;
3636
import java.beans.PropertyDescriptor;
3737
import java.io.IOException;
38+
import java.io.InputStream;
3839
import java.io.Writer;
3940
import java.lang.reflect.Array;
4041
import java.lang.reflect.Field;
@@ -44,6 +45,8 @@
4445
import java.util.*;
4546

4647
/**
48+
* A converter that converts domain classes, Maps, Lists, Arrays, POJOs and POGOs to JSON
49+
*
4750
* @author Siegfried Puchbauer
4851
*/
4952
public class JSON extends AbstractConverter implements Converter {
@@ -96,6 +99,7 @@ public JSON(Object target) {
9699
*
97100
* @param out the Writer
98101
* @throws org.codehaus.groovy.grails.web.converters.exceptions.ConverterException
102+
*
99103
*/
100104
public void render(Writer out) throws ConverterException {
101105
this.writer = new JSONWriter(out);
@@ -134,7 +138,7 @@ public void render(HttpServletResponse response) throws ConverterException {
134138
* @param idProperty The GrailsDomainClassProperty
135139
* @return
136140
*/
137-
private Object extractIdValue(Object domainObject, GrailsDomainClassProperty idProperty) {
141+
protected Object extractIdValue(Object domainObject, GrailsDomainClassProperty idProperty) {
138142
BeanWrapper beanWrapper = new BeanWrapperImpl(domainObject);
139143
return beanWrapper.getPropertyValue(idProperty.getName());
140144
}
@@ -334,25 +338,73 @@ private void property(String key, Object value) throws JSONException, ConverterE
334338
public String toString(boolean prettyPrint) throws JSONException {
335339
String json = super.toString();
336340
if (prettyPrint) {
337-
return new JSONObject(json).toString(3);
341+
Object jsonObject = new JSONTokener(json).nextValue();
342+
if (jsonObject instanceof JSONObject)
343+
return ((JSONObject) jsonObject).toString(3);
344+
else if (jsonObject instanceof JSONArray)
345+
return ((JSONArray) jsonObject).toString(3);
338346
}
339347
return json;
340348
}
341349

342350
/**
343-
* @param target
351+
* Parses the given JSON String and returns ether a JSONObject or a JSONArry
352+
*
353+
* @param source A string containing some JSON
354+
* @return ether a JSONObject or a JSONArray - depending on the given JSON
355+
* @throws ConverterException when the JSON content is not valid
344356
*/
345-
public void setTarget(Object target) {
346-
this.target = target;
357+
public static Object parse(String source) throws ConverterException {
358+
try {
359+
return new JSONTokener(source).nextValue();
360+
} catch (JSONException e) {
361+
throw new ConverterException("Error parsing JSON", e);
362+
}
363+
}
364+
365+
/**
366+
* Parses the given JSON and returns ether a JSONObject or a JSONArry
367+
*
368+
* @param is An InputStream which delivers some JSON
369+
* @param encoding the Character Encoding to use
370+
* @return ether a JSONObject or a JSONArray - depending on the given JSON
371+
* @throws ConverterException when the JSON content is not valid
372+
*/
373+
public static Object parse(InputStream is, String encoding) throws ConverterException {
374+
try {
375+
return parse(IOUtils.toString(is, encoding));
376+
} catch (IOException e) {
377+
throw new ConverterException("Error parsing JSON", e);
378+
}
379+
}
347380

381+
/**
382+
* Parses the given request's InputStream and returns ether a JSONObject or a JSONArry
383+
*
384+
* @param request the JSON Request
385+
* @return ether a JSONObject or a JSONArray - depending on the given JSON
386+
* @throws ConverterException when the JSON content is not valid
387+
*/
388+
public static Object parse(HttpServletRequest request) throws ConverterException {
389+
String encoding = request.getCharacterEncoding();
390+
if (encoding == null)
391+
encoding = Converter.DEFAULT_REQUEST_ENCODING;
392+
try {
393+
return parse(request.getInputStream(), encoding);
394+
} catch (IOException e) {
395+
throw new ConverterException("Error parsing JSON", e);
396+
}
348397
}
349398

350399
/**
351-
* @param type
352-
* @return
400+
* Sets the Object which is later converted to JSON
401+
*
402+
* @param target the Object
403+
* @see org.codehaus.groovy.grails.web.converters.Converter
353404
*/
354-
public Object asType(Class type) {
355-
return null;
405+
public void setTarget(Object target) {
406+
this.target = target;
407+
356408
}
357409

358410
}

0 commit comments

Comments
 (0)