【问题标题】:How to Render a Map as a property in a Grails 4 JSON View如何在 Grails 4 JSON 视图中将地图呈现为属性
【发布时间】:2021-11-18 04:19:38
【问题描述】:

我正在尝试呈现具有Map 属性的 JSON 视图。我希望它以 JSON 格式呈现为子对象。这似乎应该很容易,但我在任何地方都找不到它的文档。我创建了一个example project on GitHub

域类

class Breakfast {
    String meat
    String eggs
    String side
}

控制器

class FooController {
    static responseFormats = ['json', 'xml']
    
    def index() {
        Map<String, Breakfast> mealsByPerson = [
            Tom: new Breakfast(meat: "bacon", eggs: "scrambled", side: "hashbrowns"),
            Jack: new Breakfast(meat: "sausage", eggs: "over easy", side: "pancakes")
        ]

        render template: "foo", model: [cost: 12.34f, date: new Date(), mealsByPerson: mealsByPerson]
    }
}

/foo/_foo.gson

import rendermapexample.Breakfast

model {
    Float cost
    Date date
    Map<String, Breakfast> mealsByPerson
}

json {
    date date
    cost cost
    mealsByPerson mealsByPerson // HOW DO I RENDER THIS
}

这是我要渲染的 JSON

{
    "cost": 12.34,
    "date": "2021-09-25T01:11:39Z",
    "mealsByPerson": {
        "Tom": {
            "eggs": "scrambled",
            "meat": "bacon",
            "side": "hashbrowns"
        },
        "Jack": {
            "eggs": "over easy",
            "meat": "sausage",
            "side": "pancakes"
        }
    }
}

这是我得到的错误

java.lang.reflect.InvocationTargetException: null
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:211)
    at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:188)
    at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
    at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Error rendering view: null
    at grails.artefact.controller.support.ResponseRenderer$Trait$Helper.renderViewForTemplate(ResponseRenderer.groovy:617)
    at grails.artefact.controller.support.ResponseRenderer$Trait$Helper.render(ResponseRenderer.groovy:353)
    at rendermapexample.FooController.index(FooController.groovy:12)
    ... 15 common frames omitted
Caused by: grails.views.ViewRenderException: Error rendering view: null
    at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:43)
    at grails.views.mvc.GenericGroovyTemplateView.renderMergedOutputModel(GenericGroovyTemplateView.groovy:73)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:317)
    at grails.artefact.controller.support.ResponseRenderer$Trait$Helper.renderViewForTemplate(ResponseRenderer.groovy:614)
    ... 17 common frames omitted
Caused by: java.lang.StackOverflowError: null
    at grails.plugin.json.converters.InstantJsonConverter.handles(InstantJsonConverter.groovy:18)
    at grails.plugin.json.builder.DefaultJsonGenerator.findConverter(DefaultJsonGenerator.java:457)
    at grails.plugin.json.builder.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:195)
    at grails.plugin.json.builder.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:419)
...

我不能这样做:

下面的渲染语句在这个简单的例子中创建了我想要的 JSON, 但我的实际问题需要比as JSON 提供更多的渲染控制

render ([cost: 12.34f, date: new Date(), mealsByPerson: mealsByPerson] as JSON)

示例项目

https://github.com/tonyerskine/rendermapexample

更新:

我在此处发布了一个相关问题以回应以下评论:How do I render a map of domain objects using a Grails 4 JSON View

【问题讨论】:

    标签: json grails micronaut


    【解决方案1】:

    如果您将_foo.gson 更改为以下内容:

    import rendermapexample.Breakfast
    
    model {
        Float cost
        Date date
        Map<String, Breakfast> mealsByPerson
    }
    
    json {
        date date
        cost cost
        mealsByPerson g.render(mealsByPerson) {}
    }
    

    响应将包括以下内容:

    {
    "cost": 12.34,
    "date": "2021-09-27T17:24:14Z",
    "mealsByPerson": {
        "Jack": {
            "eggs": "over easy",
            "meat": "sausage",
            "side": "pancakes"
        },
        "Tom": {
            "eggs": "scrambled",
            "meat": "bacon",
            "side": "hashbrowns"
        }
    }
    

    【讨论】:

    • 有没有办法将其他属性传递给_breakfast.gson 模板?例如,mealsByPerson g.render(mealsByPerson, model: [allCaps: true]) {}
    • 是的。如果您可以描述所需的输出,我可以向您展示如何生成它。
    • 谢谢。我在描述所需输出的问题中添加了Update 1
    • “谢谢。我在描述所需输出的问题中添加了更新 1。” - 从更新编号开始表明可能还有更多。与其让这个问题成为一连串问题,不如考虑发布一个新问题来解决新问题。
    • 好点。对于那个很抱歉。我在这里发布了新问题:stackoverflow.com/q/69352644/715608
    【解决方案2】:

    您需要在“BreakFast”类中​​使用方法导出到 JSON/XML/LinkedHashMap(或其他内容),该方法可以作为字符串返回给客户端(即 getJson、getXml 等)

    要么这样,要么您需要在控制器中进行转换;我会在你的类中推荐这个方法,因为它更干净并且允许重复使用。

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    • 社区似乎明白了。只有版主似乎有问题。如果版主认为这“无用”并继续表示答案无用,我会很乐意删除答案。
    猜你喜欢
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 2011-08-08
    • 2014-08-11
    相关资源
    最近更新 更多