diff --git a/grails-converters/src/main/groovy/org/grails/web/converters/configuration/ConvertersConfigurationInitializer.java b/grails-converters/src/main/groovy/org/grails/web/converters/configuration/ConvertersConfigurationInitializer.java index e5938ba3ea5..880c15bf2ef 100644 --- a/grails-converters/src/main/groovy/org/grails/web/converters/configuration/ConvertersConfigurationInitializer.java +++ b/grails-converters/src/main/groovy/org/grails/web/converters/configuration/ConvertersConfigurationInitializer.java @@ -95,11 +95,19 @@ private void initJSONConfiguration() { marshallers.add(new org.grails.web.converters.marshaller.json.ByteArrayMarshaller()); marshallers.add(new org.grails.web.converters.marshaller.json.CollectionMarshaller()); marshallers.add(new org.grails.web.converters.marshaller.json.MapMarshaller()); - marshallers.add(new org.grails.web.converters.marshaller.json.EnumMarshaller()); - marshallers.add(new org.grails.web.converters.marshaller.ProxyUnwrappingMarshaller<>()); Config grailsConfig = getGrailsConfig(); + // Register enum marshaller - defaults to legacy for backward compatibility (will change in Grails 8.0) + String jsonEnumFormat = grailsConfig.getProperty("grails.converters.json.enum.format", String.class, "default"); + if ("simple".equals(jsonEnumFormat)) { + marshallers.add(new org.grails.web.converters.marshaller.json.SimpleEnumMarshaller()); + } else { + marshallers.add(new org.grails.web.converters.marshaller.json.EnumMarshaller()); + } + + marshallers.add(new org.grails.web.converters.marshaller.ProxyUnwrappingMarshaller<>()); + if ("javascript".equals(grailsConfig.getProperty(SETTING_CONVERTERS_JSON_DATE, String.class, "default", Arrays.asList("javascript", "default")))) { if (LOG.isDebugEnabled()) { LOG.debug("Using Javascript JSON Date Marshaller."); @@ -177,14 +185,22 @@ private void initXMLConfiguration() { marshallers.add(new org.grails.web.converters.marshaller.xml.ArrayMarshaller()); marshallers.add(new org.grails.web.converters.marshaller.xml.CollectionMarshaller()); marshallers.add(new org.grails.web.converters.marshaller.xml.MapMarshaller()); - marshallers.add(new org.grails.web.converters.marshaller.xml.EnumMarshaller()); + + Config grailsConfig = getGrailsConfig(); + + // Register enum marshaller - defaults to legacy for backward compatibility (will change in Grails 8.0) + String xmlEnumFormat = grailsConfig.getProperty("grails.converters.xml.enum.format", String.class, "default"); + if ("simple".equals(xmlEnumFormat)) { + marshallers.add(new org.grails.web.converters.marshaller.xml.SimpleEnumMarshaller()); + } else { + marshallers.add(new org.grails.web.converters.marshaller.xml.EnumMarshaller()); + } + marshallers.add(new org.grails.web.converters.marshaller.xml.DateMarshaller()); marshallers.add(new ProxyUnwrappingMarshaller<>()); marshallers.add(new org.grails.web.converters.marshaller.xml.ToStringBeanMarshaller()); ProxyHandler proxyHandler = getProxyHandler(); - Config grailsConfig = getGrailsConfig(); - boolean includeDomainVersion = includeDomainVersionProperty(grailsConfig, "xml"); if (grailsConfig.getProperty(SETTING_CONVERTERS_XML_DEEP, Boolean.class, false)) { marshallers.add(new org.grails.web.converters.marshaller.xml.DeepDomainClassMarshaller(includeDomainVersion, proxyHandler, grailsApplication)); diff --git a/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/EnumMarshaller.java b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/EnumMarshaller.java index 0b91101cdbd..283f8b81ec5 100644 --- a/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/EnumMarshaller.java +++ b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/EnumMarshaller.java @@ -30,7 +30,11 @@ /** * @author Siegfried Puchbauer * @since 1.1 + * @deprecated As of 7.0.2, replaced by {@link SimpleEnumMarshaller} for round-trip compatibility. + * This marshaller will no longer be registered by default in Grails 8.0. + * To opt-in to the new behavior now, set {@code grails.converters.json.enum.format=simple} in application.yml. */ +@Deprecated(forRemoval = true, since = "7.0.2") public class EnumMarshaller implements ObjectMarshaller { public boolean supports(Object object) { diff --git a/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/SimpleEnumMarshaller.java b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/SimpleEnumMarshaller.java new file mode 100644 index 00000000000..371d9b2af23 --- /dev/null +++ b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/json/SimpleEnumMarshaller.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.grails.web.converters.marshaller.json; + +import java.lang.reflect.Method; + +import org.springframework.beans.BeanUtils; + +import grails.converters.JSON; +import org.grails.web.converters.exceptions.ConverterException; +import org.grails.web.converters.marshaller.ObjectMarshaller; + +/** + * Marshals enums as simple string values (just the enum name) for symmetric serialization/deserialization. + * This provides round-trip compatibility where POSTing JSON returns the same format when GETting. + * + * @since 7.0.2 + */ +public class SimpleEnumMarshaller implements ObjectMarshaller { + + public boolean supports(Object object) { + return object.getClass().isEnum(); + } + + public void marshalObject(Object en, JSON json) throws ConverterException { + try { + Method nameMethod = BeanUtils.findDeclaredMethod(en.getClass(), "name"); + try { + json.convertAnother(nameMethod.invoke(en)); + } + catch (Exception e) { + json.convertAnother(""); + } + } + catch (ConverterException ce) { + throw ce; + } + catch (Exception e) { + throw new ConverterException("Error converting Enum with class " + en.getClass().getName(), e); + } + } +} diff --git a/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/EnumMarshaller.java b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/EnumMarshaller.java index 9251b283c8a..2f2a15d411c 100644 --- a/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/EnumMarshaller.java +++ b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/EnumMarshaller.java @@ -29,7 +29,11 @@ /** * @author Siegfried Puchbauer * @since 1.1 + * @deprecated As of 7.0.2, replaced by {@link SimpleEnumMarshaller} for round-trip compatibility. + * This marshaller will no longer be registered by default in Grails 8.0. + * To opt-in to the new behavior now, set {@code grails.converters.xml.enum.format=simple} in application.yml. */ +@Deprecated(forRemoval = true, since = "7.0.2") public class EnumMarshaller implements ObjectMarshaller { public boolean supports(Object object) { diff --git a/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/SimpleEnumMarshaller.java b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/SimpleEnumMarshaller.java new file mode 100644 index 00000000000..59f4901dee3 --- /dev/null +++ b/grails-converters/src/main/groovy/org/grails/web/converters/marshaller/xml/SimpleEnumMarshaller.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.grails.web.converters.marshaller.xml; + +import java.lang.reflect.Method; + +import org.springframework.beans.BeanUtils; + +import grails.converters.XML; +import org.grails.web.converters.exceptions.ConverterException; +import org.grails.web.converters.marshaller.ObjectMarshaller; + +/** + * Marshals enums as simple string values (just the enum name) for symmetric serialization/deserialization. + * This provides round-trip compatibility where POSTing XML returns the same format when GETting. + * + * @since 7.0.2 + */ +public class SimpleEnumMarshaller implements ObjectMarshaller { + + public boolean supports(Object object) { + return object.getClass().isEnum(); + } + + public void marshalObject(Object en, XML xml) throws ConverterException { + try { + Method nameMethod = BeanUtils.findDeclaredMethod(en.getClass(), "name"); + try { + xml.chars(nameMethod.invoke(en).toString()); + } + catch (Exception e) { + // ignored + } + } + catch (ConverterException ce) { + throw ce; + } + catch (Exception e) { + throw new ConverterException("Error converting Enum with class " + en.getClass().getName(), e); + } + } +} diff --git a/grails-test-suite-web/src/test/groovy/org/grails/web/converters/JSONConverterTests.groovy b/grails-test-suite-web/src/test/groovy/org/grails/web/converters/JSONConverterTests.groovy index ea6e5fa043f..600084bb509 100644 --- a/grails-test-suite-web/src/test/groovy/org/grails/web/converters/JSONConverterTests.groovy +++ b/grails-test-suite-web/src/test/groovy/org/grails/web/converters/JSONConverterTests.groovy @@ -103,6 +103,26 @@ class JSONConverterTests extends Specification implements ControllerUnitTest