如何在 Grails 4 JSON 视图中将地图渲染为 属性

How to Render a Map as a property in a Grails 4 JSON View

我正在尝试渲染一个 JSON 视图,其中 属性 是 Map。我希望它作为子对象在 JSON 中呈现。这看起来应该很容易,但我无法在任何地方找到它的记录。我创建了一个 example project on GitHub.

域名Class

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.WhosebugError: 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

更新:

我在这里发布了一个相关问题以回应下面的评论:

您的 'BreakFast' class 中需要一个方法来导出到 JSON/XML/LinkedHashMap(或其他),它可以在对客户端的响应中作为字符串返回(即 getJson, getXml 等)

或者您需要在控制器中进行转换;我会建议您 class 中的方法,因为它更干净并且允许重复使用。

如果将 _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"
    }
}