codeconsole commented on PR #16237:
URL: https://github.com/apache/grails-core/pull/16237#issuecomment-5459403452
## Deprecation migration examples
This comment maps every API deprecated by this PR to its modern replacement.
Overloads are grouped where their migration is identical.
> The XML migrations intentionally change the response representation to
JSON. The deprecated XML implementations remain available in `grails-xml` for
clients that cannot migrate immediately.
### 1. `JSON.registerObjectMarshaller(...)` (all four overloads)
This covers `(Class, Closure)`, `(Class, int, Closure)`,
`(ObjectMarshaller)`, and `(ObjectMarshaller, int)`.
Before:
```groovy
JSON.registerObjectMarshaller(Book, 100) { Book book ->
[id: book.id, title: book.title]
}
```
After, for an application-wide Jackson representation:
```groovy
import
org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
import org.springframework.context.annotation.Bean
import tools.jackson.databind.module.SimpleModule
@Bean
JsonMapperBuilderCustomizer bookJsonCustomizer() {
{ builder ->
builder.addModule(new SimpleModule()
.addSerializer(Book, new BookValueSerializer()))
} as JsonMapperBuilderCustomizer
}
```
`BookValueSerializer` is a Jackson 3 `ValueSerializer<Book>`. Jackson module
registration replaces legacy marshaller priority; serializer/module ordering
should be made explicit when serializers overlap.
### 2. `JSON.withDefaultConfiguration(...)`
Before:
```groovy
JSON.withDefaultConfiguration {
it.registerObjectMarshaller(Book) { Book book ->
[id: book.id, title: book.title]
}
}
```
After:
```groovy
@Bean
JsonMapperBuilderCustomizer bookJsonCustomizer() {
{ builder ->
builder.addModule(new SimpleModule()
.addSerializer(Book, new BookValueSerializer()))
} as JsonMapperBuilderCustomizer
}
```
This customizes Spring Boot's managed `JsonMapper`, so the representation is
shared by `respond`, Spring MVC message conversion, and other Jackson
integrations.
### 3. `JSON.createNamedConfig(...)`
Before:
```groovy
JSON.createNamedConfig('deep') {
it.registerObjectMarshaller(Type, deepObjectMarshaller)
it.registerObjectMarshaller(Category, deepObjectMarshaller)
}
```
After:
```groovy
import grails.converters.json.NamedJsonConfigurationRegistry
class BootStrap {
NamedJsonConfigurationRegistry namedJsonConfigurationRegistry
def init = { servletContext ->
namedJsonConfigurationRegistry.register('deep') {
it.serializer(Type, new TypeValueSerializer())
it.serializer(Category, new CategoryValueSerializer())
}
}
}
```
The named configuration derives an isolated mapper from Boot's configured
`JsonMapper`; it does not mutate global or thread-local converter state.
### 4. `JSON.use(String, Closure)`
Before:
```groovy
String json = JSON.use('deep') {
new JSON(book).toString()
}
```
After, for direct serialization:
```groovy
String json = namedJsonConfigurationRegistry.writeValueAsString('deep', book)
```
Or stream directly:
```groovy
namedJsonConfigurationRegistry.writeValue('deep', writer, book)
```
For controller responses, the same configuration works with both APIs:
```groovy
render book, jsonConfiguration: 'deep'
respond book, jsonConfiguration: 'deep'
```
### 5. `JSON.use(String)`
Before:
```groovy
JSON.use('deep')
try {
String json = new JSON(book).toString()
} finally {
JSON.use('default')
}
```
After:
```groovy
String json = namedJsonConfigurationRegistry.writeValueAsString('deep', book)
```
Configuration is selected explicitly per operation, eliminating mutable
thread-local state.
### 6. `JSON.getNamedConfig(String)`
Before:
```groovy
def configuration = JSON.getNamedConfig('deep')
```
After:
```groovy
ObjectWriter writer = namedJsonConfigurationRegistry.writer('deep')
String json = writer.writeValueAsString(book)
```
Registration remains encapsulated in
`NamedJsonConfigurationRegistry.register(...)`; consumers receive the
configured, immutable-style Jackson writer used for serialization.
### 7. `HalXmlRenderer`
Before:
```groovy
import grails.rest.render.hal.HalXmlRenderer
beans = {
halBookRenderer(HalXmlRenderer, Book)
}
```
After, using Grails HAL JSON:
```groovy
import grails.rest.render.hal.HalJsonRenderer
beans = {
halBookRenderer(HalJsonRenderer, Book)
}
```
Clients negotiate it with:
```http
Accept: application/hal+json
```
Applications that prefer Spring's hypermedia model can instead add the
optional `grails-spring-hateoas` module.
### 8. `HalXmlCollectionRenderer`
Before:
```groovy
import grails.rest.render.hal.HalXmlCollectionRenderer
beans = {
halBooksRenderer(HalXmlCollectionRenderer, Book)
}
```
After:
```groovy
import grails.rest.render.hal.HalJsonCollectionRenderer
beans = {
halBooksRenderer(HalJsonCollectionRenderer, Book)
}
```
Clients use `Accept: application/hal+json` rather than `application/hal+xml`.
### 9. `VndErrorXmlRenderer`
Before:
```groovy
import grails.rest.render.errors.VndErrorXmlRenderer
beans = {
vndXmlErrorRenderer(VndErrorXmlRenderer)
}
// Client: Accept: application/vnd.error+xml
respond book.errors
```
After, using the default RFC 9457 validation response:
```groovy
// No Vnd.Error renderer registration is required.
// Client: Accept: application/json or application/problem+json
respond book.errors
```
The response uses `application/problem+json` and status 422 for validation
failures.
### 10. XML `ValidationErrorsMarshaller`
Before:
```groovy
import grails.converters.XML
import org.grails.web.converters.marshaller.xml.ValidationErrorsMarshaller
XML.registerObjectMarshaller(new ValidationErrorsMarshaller(), 100)
render book.errors as XML
```
After:
```groovy
// Client: Accept: application/json or application/problem+json
respond book.errors
```
Grails renders the validation errors as RFC 9457 `application/problem+json`.
Keep the deprecated marshaller only while an existing client still requires the
legacy XML error schema.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]