【问题标题】:Problem creating JSON with JsonGroovyBuilder in Groovy在 Groovy 中使用 JsonGroovyBuilder 创建 JSON 时出现问题
【发布时间】:2010-02-18 08:27:14
【问题描述】:

我是 JSON 和 Groovy 的新手。我可以对 groovlet 进行 AJAX 调用,并使用 MarkupBuilder 构建一些 HTML 代码。随着返回的 HTML 字符串,我希望将 JSON 字符串填充到输入文本框之一。问题在于 JsonGroovyBuilder() 的使用。我什至无法让 Json-lib 中显示的最简单示例正确运行。这是我的代码:

import net.sf.json.groovy.*;
import net.sf.json.groovy.JsonGroovyBuilder;

def builder = new JsonGroovyBuilder()  
def books = builder.books {  
    book = [title: "Groovy in Action", author: "Dierk Konig"]  
    book = [title: "Groovy in Action", author: "Dierk Konig"]  
}  

我在 GroovyConsole 上运行这段简单的代码,我得到了回报:

Result: {"books":null}

很奇怪。但更“奇怪”的是,当我在 Eclipse 中运行它时,我得到了这个:

Caught: groovy.lang.MissingMethodException: No signature of method: net.sf.json.groovy.JsonGroovyBuilder.books() is applicable for argument types: (JSONSandbox$_run_closure1) values: [JSONSandbox$_run_closure1@164debb]
Possible solutions: is(java.lang.Object)
 at JSONSandbox.run(JSONSandbox.groovy:6)

我想我有我需要的所有 jar 文件:

    json-lib-2.3-jdk15.jar
commons-collections-2.1.jar
commons-lang-2.3.jar
httpclient-4.0.1.jar

我已经被这个问题困扰了几天了。也许我做错了什么,或者误解了这个功能的使用。我在 JSON 和 Groovy 上搜索的所有地方都指向 Grails。我还是 Groovy 的新手,我不了解 Grails。我不想扔掉我的 Groovy 代码并重新开始。有什么办法可以解决这个问题?非常感谢!

【问题讨论】:

    标签: json groovy


    【解决方案1】:

    我从未尝试过使用JsonGroovyBuilder,因此无法真正帮助您。 Grails 1.1 提供的 JSON Builder(在 Grails 1.2 中被更好的版本取代)让我同样感到沮丧。我通过编写自己的 Groovy JSON 构建器克服了这种挫败感,欢迎您使用。我已经粘贴了下面的源代码。

    import org.json.JSONStringer
    
    /**
     * An alternative JSON builder, because <code>grails.util.JSonBuilder</code> sucks!
     * The reasons why it sucks are described here: http://www.anyware.co.uk/2005/2008/06/19/a-grails-json-builder-that-doesnt-suck/
     * Although this page provides an alternative JSON builder, the author didn't provide any usage examples, and I couldn't
     * figure out how to use it, so I wrote my own instead.
     *
     * The underlying JSON functionality is provided by <code>json.org.JSONStringer</code>. An example usage is:
     *
     * <code>
     * builder.array(['feck', 'arse', 'girls']) {
     *     foo(bar:'1', baz: '2') {
     *         hobbies(sport: 'snooker', drink: 'guinness')
     *         emptyObj([:])
     *         emptyArray([])
     *     }
     * }
     * builder.json
     * </code>
     *
     * This will print:
     * <code>
     * ["feck","arse","girls", {"bar":"1", "baz":"2", "hobbies": {"sport":"snooker", "drink":"guinness"}, "emptyObj": {},"emptyArray":[]}]
     * </code>
     *
     * Verifiable usage examples are provided by the unit tests. A few points worth noting (the term 'element' is used below
     * to mean 'either a JSON object or JSON array'):
     *
     * <ul>
     *   <li>The nesting of elements is indicated by the nesting of closures in the usual Groovy builder fashion</li>
     *   <li>The name of the method is used as the name of the key when nesting an element within an object</li>
     *   <li>The name of the method is irrelevant when nesting an element within an array, but it is recommended
     *   to use either the method name 'object' or 'array' for the sake of code readability</li>
     *   <li>The decision to create an array or object is determined by the type of the method parameter. A map will cause
     *   an object to be created, any other type will cause an array to be created</li>
     *   <li>To create an empty array or an array whose contents are determined only by nested closures, either call
     *   <code>builder.array()</code> or <code>builder.keyName([])</code>. The latter should be used when nesting the empty
     *   array within an object and control over the key name is required.</li>
     *   <li>To create an empty object or an object whose contents are determined only by nested closures, either call
     *   <code>builder.object()</code> or <code>builder.keyName([:])</code>. The latter should be used when nesting the empty
     *   object within another object and control over the key name is required.</li>
     * </ul>
     */
    class SimpleJSONBuilder extends BuilderSupport {
    
        private jsonText = new JSONStringer()
    
        /**
         * Returns the JSON text created by this builder
         */
        String getJson() {
            jsonText.toString()
        }
    
        String toString() {
            getJson()
        }
    
        protected void setParent(Object parent, Object child) {
            // Method is abstract in parent class, but an empty implementation is all we need
        }
    
        /**
         * Creates an array or object which is either empty, or whose contents are determined exclusively by nested closures.
         */
        protected Object createNode(Object name) {
    
            if (current == ElementType.OBJECT) {
                throw new IllegalStateException("""Error processing method $name() Empty argument methods should not be invoked
    when nesting an element within an object because the key name cannot be determined. Replace this call with either
    $name([]) or $name([:])""")
            }
    
            if (name == 'array') {
                jsonText.array()
                return ElementType.ARRAY
    
            } else if (name == 'object') {
                jsonText.object()
                return ElementType.OBJECT
    
            } else {
                throw new IllegalArgumentException("""When calling a method with no arguments, the method must be named either
    '$array' or '$object' to indicate which you wish to create""")
            }
    
        }
    
        protected Object createNode(Object name, Map attributes, Object value) {
            throw new UnsupportedOperationException("Error invoking method $name. Method calls must supply either a single object (to create an array) or a Map (to create an object)")
        }
    
        /**
         * Ensures that an array/object is correctly nested within an object
         * @name Name of the key to use for the nested element
         * @return The type of element
         */
        private void nestElement(name) {
            if (current == ElementType.OBJECT) {
                jsonText.key(name)
            }
        }
    
        /**
         * Creates an array
         * @name Name of the method. This will be used as the key if the array is nested within an object
         * @value The contents of the array. This should be either a single value or a collection or array
         * @return The type of element
         */
        protected Object createNode(Object name, Object value) {
            nestElement(name)
            jsonText.array()
    
            if (value instanceof Collection || value instanceof Object[]) {
                value.each {jsonText.value(it)}
    
            } else {
                jsonText.value(value)
            }
            return ElementType.ARRAY
        }
    
        /**
         * Creates an object
         * @name Name of the method. This will be used as the key if the object is nested within an object
         * @value The name-value pairs contained by this object
         * @return The type of element
         */
        protected Object createNode(Object name, Map attributes) {
            nestElement(name)
            jsonText.object()
            attributes.each {key, value ->
                jsonText.key(key).value(value)
            }
            return ElementType.OBJECT
        }
    
        protected void nodeCompleted(Object parent, Object node) {
            node == ElementType.OBJECT ? jsonText.endObject() : jsonText.endArray()
        }
    }
    
    private enum ElementType {
        ARRAY, OBJECT
    }
    

    上面的源代码定义了类SimpleJSONBuilder和枚举SimpleJSONBuilder,但是你可以将这两个存储在一个文件SimpleJSONBuilder.groovy中。

    此构建器所需的唯一库是json.org 提供的Java JSON 库。

    如果上面代码中的 cmets 没有很好地解释如何使用它,这里有一些测试用例:

    public class SimpleJSONBuilderTests extends GroovyTestCase {
    
        void testRootArrayElement() {
            def builder = new SimpleJSONBuilder()
            builder.array(['feck', 'arse', 'girls']) {
                foo(bar: '1', baz: '2') {
                    hobbies(sport: 'snooker', drink: 'guinness')
                    emptyObj([:])
                    emptyArray([])
                }
            }
    
            assertEquals builder.json, '["feck","arse","girls",{"bar":"1","baz":"2","hobbies":{"sport":"snooker","drink":"guinness"},"emptyObj":{},"emptyArray":[]}]'
        }
    
        void testRootObjElement() {
           def builder = new SimpleJSONBuilder()
            builder.object(feck:'arse') {
                foo(bar: '1', baz: '2') {
                    hobbies(sport: 'snooker', drink: 'guinness')
                    emptyObj([:])
                    emptyArray([])
                }
            }
    
            assertEquals builder.json, '{"feck":"arse","foo":{"bar":"1","baz":"2","hobbies":{"sport":"snooker","drink":"guinness"},"emptyObj":{},"emptyArray":[]}}'
        }        
    
        /**
         * Test that both mechanisms for creating empty arrays are equivalent
         */
        void testEmptyArrays() {
            def builder = new SimpleJSONBuilder()
            builder.array([])
    
            def builder2 = new SimpleJSONBuilder()
            builder2.array()
    
            assertEquals builder.json, builder2.json
            assertEquals builder.json, "[]"
        }
    
        /**
         * Test that both mechanisms for creating empty objects are equivalent
         */
        void testEmptyObjects() {
            def builder = new SimpleJSONBuilder()
            builder.object([:])
    
            def builder2 = new SimpleJSONBuilder()
            builder2.object()
    
            assertEquals builder.json, builder2.json
            assertEquals builder.json, "{}"                
        }    
    }
    

    【讨论】:

    • 爱父亲 Ted 启发的测试用例:-D
    【解决方案2】:

    这是 JsonGroovyBuilder 中的一个小错误。如果您从脚本中使用它,则使用脚本的绑定解析未知属性。 我已经为 JsonBuilder 发布了一个补丁,所以我希望这个 bug 将很快得到解决。 详情请见https://sourceforge.net/tracker/?func=detail&aid=3022114&group_id=171425&atid=857928

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-19
      • 2020-11-13
      • 2016-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-26
      相关资源
      最近更新 更多