2

我正在将一个非常旧的 Grails 应用程序转换为最新版本 (3.3.0)。事情有点令人沮丧,但我已经非常接近迁移除了 JSON 和 XML 编组器之外的所有内容,它们之前在我的 BootStrap init 中注册。

之前的 marshaller 注册看起来像这样:

// register JSON marshallers at startup in all environments
MarshallerUtils.registerMarshallers()

这是这样定义的:

class MarshallerUtils {

    // Registers marshaller logic for various types that
    // aren't supported out of the box or that we want to customize.
    // These are used whenever the JSON or XML converters are called,
    // e.g. return model as JSON
    static registerMarshallers() {

        final dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis()
        final isoDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

        // register marshalling logic for both XML and JSON converters
        [XML, JSON].each { converter ->

            // This overrides the marshaller from the joda time plugin to
            // force all DateTime instances to use the UTC time zone
            // and the ISO standard "yyyy-mm-ddThh:mm:ssZ" format
            converter.registerObjectMarshaller(DateTime, 10) { DateTime it ->
                return it == null ? null : it.toString(dateTimeFormatter.withZone(org.joda.time.DateTimeZone.UTC))
            }

            converter.registerObjectMarshaller(Date, 10) { Date it ->
                return it == null ? null : isoDateFormat.format(it)
            }

            converter.registerObjectMarshaller(TIMESTAMP, 10) { TIMESTAMP it ->
                return it == null ? null : isoDateFormat.format(it.dateValue())
            }
        }
    }
}

在迁移过程中,我最终将 org.joda.time.DateTime 的所有实例转换为 java.time.ZonedDateTime:

class MarshallerUtils {

    // Registers marshaller logic for various types that
    // aren't supported out of the box or that we want to customize.
    // These are used whenever the JSON or XML converters are called,
    // e.g. return model as JSON
    static registerMarshallers() {

        final dateTimeFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME
        final isoDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

        // register marshalling logic for both XML and JSON converters
        [XML, JSON].each { converter ->

            // This overrides the marshaller from the java.time to
            // force all DateTime instances to use the UTC time zone
            // and the ISO standard "yyyy-mm-ddThh:mm:ssZ" format
            converter.registerObjectMarshaller(ZonedDateTime, 10) { ZonedDateTime it ->

                return it == null ? null : it.toString(dateTimeFormatter.withZone(ZoneId.of("UTC")))
            }

            converter.registerObjectMarshaller(Date, 10) { Date it ->
                return it == null ? null : isoDateFormat.format(it)
            }

            converter.registerObjectMarshaller(TIMESTAMP, 10) { TIMESTAMP it ->
                return it == null ? null : isoDateFormat.format(it.dateValue())
            }
        }
    }
}

不幸的是,在升级到 Grails 3.3.0 之后,无论我尝试做什么,这个 marshaller 注册似乎都没有被使用。

我确实知道有一种新的“JSON 视图”做事方式,但是这个特定的服务有很多端点,如果一切都已经完成,我不想为所有端点编写自定义转换器和“.gson”模板以我需要的格式。我只需要 JSON 格式的响应和行为属性的日期(格式化字符串)。

相反,我发现(与以前的行为相比,使用 ZonedDateTime 的属性在我的 JSON 输出中“爆炸”了。有大量不需要的垃圾日期对象信息,并且它没有被格式化为正如我所料,一个简单的字符串。

我已经尝试了一些东西(主要是根据官方最新 Grails 文档中的建议)---

自定义转换器 默认日期格式

在 application.yml 中为 grails 视图添加配置:

views:
    json:
        generator:
            dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
            locale: "en/US"
            timeZone: "GMT"

在“src”下创建此路径:

src/main/resources/META-INF/services/grails.plugin.json.builder.JsonGenerator$Converter

并将转换器添加到我在上面文件中命名的域类中^:

class MultibeamFileConverter implements JsonGenerator.Converter {

    final DateTimeFormatter isoDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(ZoneId.of("UTC"));

    @Override
    boolean handles(Class<?> type) {
        MultibeamFile.isAssignableFrom(type)
    }

    @Override
    Object convert(Object value, String key) {
        MultibeamFile multibeamFile = (MultibeamFile)value
        multibeamFile.startTime.format(isoDateFormat)
        multibeamFile.endTime.format(isoDateFormat)
        return multibeamFile
    }
}

在我的控制器中,我改变了:

return multibeamCatalogService.findFiles(cmd, params)

为此(为了像以前一样在浏览器中获取 JSON 输出):

respond multibeamCatalogService.findFiles(cmd, params), formats: ['json', 'xml']

不幸的是,我能想到的上述大多数排列都导致了诸如“无法解析视图”之类的错误。否则,当我收到回复时,主要问题是日期未格式化为字符串。此功能以前由 Marshaller 执行。

我越来越沮丧。有人能告诉我如何在我的 JSON 输出中将 ZonedDateTime 格式化为一个简单的字符串(例如 - “2009-06-21T00:00:00Z”),而不是像这样的巨型对象吗?简单地转换为 java.util.Date 会导致“无法解析视图”错误再次出现;因此,这期望我制作一个“.gson”视图,该视图永远不会显示我期望的格式或为空。

"startTime": {
"dayOfMonth": 26,
"dayOfWeek": {
"enumType": "java.time.DayOfWeek",
"name": "FRIDAY"
},
"dayOfYear": 207,
"hour": 0,
"minute": 0,
"month": {
"enumType": "java.time.Month",
"name": "JULY"
},
"monthValue": 7,
"nano": 0,
"offset": {
"id": "-06:00",
"rules": {
"fixedOffset": true,
"transitionRules": [],
"transitions": []
},
"totalSeconds": -21600
}, ... // AND SO ON FOR_EVAH
4

1 回答 1

0

简单的答案是格式化一个名为 .format(DateTimeFormatter) 的 ZonedDateTime 对象。这取决于你想要什么格式。您可以指定自己的或使用 DateTimeFormatter 中的一些预定义的。

我也很想知道是否有一种简单的方法可以说“每个端点都将其显示为 json”。到目前为止,我发现的唯一方法是在每个控制器类中都有这个,这还不错,但看起来很傻。我正在使用响应,然后在我的控制器方法中返回。

static responseFormats = ['json'] // This is needed for grails to indicate what format to use for respond.

虽然我仍然看到记录的错误,但 rest api 似乎仍然有效,“无法解析视图”对于我点击的任何端点。

于 2018-03-14T20:45:55.940 回答