pappmiku opened a new issue, #14931:
URL: https://github.com/apache/grails-core/issues/14931

   ### Expected Behavior
   
   When a controller attempts to respond with an object implementing 
`grails.validation.Validateable`, validation objects should not be serialized 
(possibly with the exception of the `errors` field, which was the case in 
Grails 6.2.3, but I assume no actual validation was being performed).
   
   ### Actual Behaviour
   
   Attempting to respond with a validateable object results in incorrect 
serialization and/or an exception. It seems that non-string fields cause 
exceptions (see below; id is a Long), with them removed the serialization 
passes, but there are two extra fields - `constraints` and `constraintsMap` - 
included in the resulting json.
   
   Is this an intended behavior going forward? I know it doesn't necessarily 
make sense to even have validateable response dtos, but this is the code I have 
to work with. 😄 Though a lot of them are used as both requests and responses...
   
   <details>
     <summary>Stacktrace:</summary>
   
   ```
   java.lang.reflect.InvocationTargetException: null
        at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at 
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at 
org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:210)
        at 
org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187)
        at 
org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:108)
        at 
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
        at 
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
        at 
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
        at 
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
        at 
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
        at 
org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:736)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1622)
        at 
org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:195)
        at 
org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
        at 
org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:80)
        at 
org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
        at 
org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70)
        at 
org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
        at 
org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
        at 
org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
        at 
org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1555)
        at 
org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:823)
        at 
org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:440)
        at 
org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:470)
        at 
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:575)
        at 
org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:717)
        at 
org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1071)
        at org.eclipse.jetty.server.Handler$Wrapper.handle(Handler.java:740)
        at 
org.eclipse.jetty.server.handler.EventsHandler.handle(EventsHandler.java:81)
        at org.eclipse.jetty.server.Server.handle(Server.java:182)
        at 
org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:678)
        at 
org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:416)
        at 
org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)
        at 
org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
        at 
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:981)
        at 
org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1211)
        at 
org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1166)
        at java.base/java.lang.Thread.run(Thread.java:840)
   Caused by: org.grails.web.converters.exceptions.ConverterException: Error 
converting Bean with class grails.validation.ConstrainedDelegate
        at 
org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:88)
        at 
org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:42)
        at grails.converters.JSON.value(JSON.java:187)
        at grails.converters.JSON.convertAnother(JSON.java:147)
        at 
org.grails.web.converters.marshaller.json.MapMarshaller.marshalObject(MapMarshaller.java:48)
        at 
org.grails.web.converters.marshaller.json.MapMarshaller.marshalObject(MapMarshaller.java:33)
        at grails.converters.JSON.value(JSON.java:187)
        at grails.converters.JSON.convertAnother(JSON.java:147)
        at 
org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:70)
        at 
org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:42)
        at grails.converters.JSON.value(JSON.java:187)
        at grails.converters.JSON.render(JSON.java:122)
        at 
org.grails.plugins.web.rest.render.json.DefaultJsonRenderer.renderJson(DefaultJsonRenderer.groovy:119)
        at 
org.grails.plugins.web.rest.render.json.DefaultJsonRenderer.renderJson(DefaultJsonRenderer.groovy:113)
        at 
org.grails.plugins.web.rest.render.json.DefaultJsonRenderer.render(DefaultJsonRenderer.groovy:94)
        at 
grails.artefact.controller.RestResponder$Trait$Helper.internalRespond(RestResponder.groovy:218)
        at 
grails.artefact.controller.RestResponder$Trait$Helper.respond(RestResponder.groovy:124)
        at 
org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
        at 
org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
        at 
org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
        at com.example.TestController.testFail(TestController.groovy:14)
        ... 46 common frames omitted
   Caused by: java.lang.reflect.InvocationTargetException: null
        at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at 
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at 
org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:68)
        ... 66 common frames omitted
   Caused by: groovy.lang.MissingPropertyException: CreditCard constraint only 
applies to a String property
        at 
grails.gorm.validation.DefaultConstrainedProperty.isCreditCard(DefaultConstrainedProperty.groovy:403)
        at 
grails.validation.ConstrainedDelegate.isCreditCard(ConstrainedDelegate.groovy:123)
        ... 69 common frames omitted
   ```
   
   </details>
   
   <details>
     <summary>JSON generated in Grails 6.2.3:</summary>
     
   ```json
   {
     "companies" : [ {
       "errors" : {
         "errors" : [ ]
       },
       "id" : 1,
       "info" : "",
       "name" : "."
     }, {
       "errors" : {
         "errors" : [ ]
       },
       "id" : 2,
       "info" : "",
       "name" : ""
     } ],
     "timestamp" : "2025-07-22T13:15:42.81+02:00"
   }
   
   ```
   </details>
   
   <details>
     <summary>JSON generated in Grails 7:</summary>
     
   ```json
   {
     "companies" : [ {
       "constraints" : {
         "delegate" : "cz.actis.exevido.admin.CompanyDTO",
         "directive" : 0,
         "maximumNumberOfParameters" : 1,
         "owner" : "cz.actis.exevido.admin.CompanyDTO",
         "parameterTypes" : [ "java.lang.Object" ],
         "resolveStrategy" : 0,
         "thisObject" : "cz.actis.exevido.admin.CompanyDTO"
       },
       "constraintsMap" : {
         "name" : {
           "appliedConstraints" : [ {
             "name" : "nullable",
             "nullable" : true,
             "parameter" : true,
             "propertyName" : "name",
             "valid" : true
           }, {
             "blank" : true,
             "name" : "blank",
             "parameter" : true,
             "propertyName" : "name",
             "valid" : true
           } ],
           "blank" : true,
           "creditCard" : false,
           "display" : true,
           "editable" : true,
           "email" : false,
           "format" : null,
           "inList" : null,
           "matches" : null,
           "max" : null,
           "maxSize" : null,
           "min" : null,
           "minSize" : null,
           "notEqual" : null,
           "nullable" : true,
           "order" : 2,
           "owner" : "cz.actis.exevido.admin.CompanyDTO",
           "password" : false,
           "property" : {
             "appliedConstraints" : [ {
               "name" : "nullable",
               "nullable" : true,
               "parameter" : true,
               "propertyName" : "name",
               "valid" : true
             }, {
               "blank" : true,
               "name" : "blank",
               "parameter" : true,
               "propertyName" : "name",
               "valid" : true
             } ],
             "attributes" : { },
             "blank" : true,
             "creditCard" : false,
             "display" : true,
             "editable" : true,
             "email" : false,
             "format" : null,
             "inList" : null,
             "matches" : null,
             "max" : null,
             "maxSize" : null,
             "metaConstraints" : { },
             "min" : null,
             "minSize" : null,
             "notEqual" : null,
             "nullable" : true,
             "order" : 2,
             "owner" : "cz.actis.exevido.admin.CompanyDTO",
             "owningClass" : "cz.actis.exevido.admin.CompanyDTO",
             "password" : false,
             "propertyName" : "name",
             "propertyType" : "java.lang.String",
             "range" : null,
             "scale" : null,
             "size" : null,
             "url" : false,
             "widget" : null
           },
           "propertyName" : "name",
           "propertyType" : "java.lang.String",
           "range" : null,
           "scale" : null,
           "size" : null,
           "url" : false,
           "widget" : null
         },
         "info" : {
           "appliedConstraints" : [ {
             "name" : "nullable",
             "nullable" : true,
             "parameter" : true,
             "propertyName" : "info",
             "valid" : true
           }, {
             "blank" : true,
             "name" : "blank",
             "parameter" : true,
             "propertyName" : "info",
             "valid" : true
           } ],
           "blank" : true,
           "creditCard" : false,
           "display" : true,
           "editable" : true,
           "email" : false,
           "format" : null,
           "inList" : null,
           "matches" : null,
           "max" : null,
           "maxSize" : null,
           "min" : null,
           "minSize" : null,
           "notEqual" : null,
           "nullable" : true,
           "order" : 3,
           "owner" : "cz.actis.exevido.admin.CompanyDTO",
           "password" : false,
           "property" : {
             "appliedConstraints" : [ {
               "name" : "nullable",
               "nullable" : true,
               "parameter" : true,
               "propertyName" : "info",
               "valid" : true
             }, {
               "blank" : true,
               "name" : "blank",
               "parameter" : true,
               "propertyName" : "info",
               "valid" : true
             } ],
             "attributes" : { },
             "blank" : true,
             "creditCard" : false,
             "display" : true,
             "editable" : true,
             "email" : false,
             "format" : null,
             "inList" : null,
             "matches" : null,
             "max" : null,
             "maxSize" : null,
             "metaConstraints" : { },
             "min" : null,
             "minSize" : null,
             "notEqual" : null,
             "nullable" : true,
             "order" : 3,
             "owner" : "cz.actis.exevido.admin.CompanyDTO",
             "owningClass" : "cz.actis.exevido.admin.CompanyDTO",
             "password" : false,
             "propertyName" : "info",
             "propertyType" : "java.lang.String",
             "range" : null,
             "scale" : null,
             "size" : null,
             "url" : false,
             "widget" : null
           },
           "propertyName" : "info",
           "propertyType" : "java.lang.String",
           "range" : null,
           "scale" : null,
           "size" : null,
           "url" : false,
           "widget" : null
         },
         "id" : {
           "appliedConstraints" : [ {
             "name" : "nullable",
             "nullable" : true,
             "parameter" : true,
             "propertyName" : "id",
             "valid" : true
           } ],
           "blank" : true,
           "creditCard" : false,
           "display" : true,
           "editable" : true,
           "email" : false,
           "format" : null,
           "inList" : null,
           "matches" : null,
           "max" : null,
           "maxSize" : null,
           "min" : null,
           "minSize" : null,
           "notEqual" : null,
           "nullable" : true,
           "order" : 4,
           "owner" : "cz.actis.exevido.admin.CompanyDTO",
           "password" : false,
           "property" : {
             "appliedConstraints" : [ {
               "name" : "nullable",
               "nullable" : true,
               "parameter" : true,
               "propertyName" : "id",
               "valid" : true
             } ],
             "attributes" : { },
             "blank" : true,
             "creditCard" : false,
             "display" : true,
             "editable" : true,
             "email" : false,
             "format" : null,
             "inList" : null,
             "matches" : null,
             "max" : null,
             "maxSize" : null,
             "metaConstraints" : { },
             "min" : null,
             "minSize" : null,
             "notEqual" : null,
             "nullable" : true,
             "order" : 4,
             "owner" : "cz.actis.exevido.admin.CompanyDTO",
             "owningClass" : "cz.actis.exevido.admin.CompanyDTO",
             "password" : false,
             "propertyName" : "id",
             "propertyType" : "java.lang.String",
             "range" : null,
             "scale" : null,
             "size" : null,
             "url" : false,
             "widget" : null
           },
           "propertyName" : "id",
           "propertyType" : "java.lang.String",
           "range" : null,
           "scale" : null,
           "size" : null,
           "url" : false,
           "widget" : null
         }
       },
       "errors" : {
         "errors" : [ ]
       },
       "id" : "",
       "info" : "",
       "name" : ""
     }, {
       "constraints" : {
         "delegate" : "cz.actis.exevido.admin.CompanyDTO",
         "directive" : 0,
         "maximumNumberOfParameters" : 1,
         "owner" : "cz.actis.exevido.admin.CompanyDTO",
         "parameterTypes" : [ "java.lang.Object" ],
         "resolveStrategy" : 0,
         "thisObject" : "cz.actis.exevido.admin.CompanyDTO"
       },
       "constraintsMap" : {
         "name" : {
           "appliedConstraints" : [ {
             "name" : "nullable",
             "nullable" : true,
             "parameter" : true,
             "propertyName" : "name",
             "valid" : true
           }, {
             "blank" : true,
             "name" : "blank",
             "parameter" : true,
             "propertyName" : "name",
             "valid" : true
           } ],
           "blank" : true,
           "creditCard" : false,
           "display" : true,
           "editable" : true,
           "email" : false,
           "format" : null,
           "inList" : null,
           "matches" : null,
           "max" : null,
           "maxSize" : null,
           "min" : null,
           "minSize" : null,
           "notEqual" : null,
           "nullable" : true,
           "order" : 2,
           "owner" : "cz.actis.exevido.admin.CompanyDTO",
           "password" : false,
           "property" : {
             "appliedConstraints" : [ {
               "name" : "nullable",
               "nullable" : true,
               "parameter" : true,
               "propertyName" : "name",
               "valid" : true
             }, {
               "blank" : true,
               "name" : "blank",
               "parameter" : true,
               "propertyName" : "name",
               "valid" : true
             } ],
             "attributes" : { },
             "blank" : true,
             "creditCard" : false,
             "display" : true,
             "editable" : true,
             "email" : false,
             "format" : null,
             "inList" : null,
             "matches" : null,
             "max" : null,
             "maxSize" : null,
             "metaConstraints" : { },
             "min" : null,
             "minSize" : null,
             "notEqual" : null,
             "nullable" : true,
             "order" : 2,
             "owner" : "cz.actis.exevido.admin.CompanyDTO",
             "owningClass" : "cz.actis.exevido.admin.CompanyDTO",
             "password" : false,
             "propertyName" : "name",
             "propertyType" : "java.lang.String",
             "range" : null,
             "scale" : null,
             "size" : null,
             "url" : false,
             "widget" : null
           },
           "propertyName" : "name",
           "propertyType" : "java.lang.String",
           "range" : null,
           "scale" : null,
           "size" : null,
           "url" : false,
           "widget" : null
         },
         "info" : {
           "appliedConstraints" : [ {
             "name" : "nullable",
             "nullable" : true,
             "parameter" : true,
             "propertyName" : "info",
             "valid" : true
           }, {
             "blank" : true,
             "name" : "blank",
             "parameter" : true,
             "propertyName" : "info",
             "valid" : true
           } ],
           "blank" : true,
           "creditCard" : false,
           "display" : true,
           "editable" : true,
           "email" : false,
           "format" : null,
           "inList" : null,
           "matches" : null,
           "max" : null,
           "maxSize" : null,
           "min" : null,
           "minSize" : null,
           "notEqual" : null,
           "nullable" : true,
           "order" : 3,
           "owner" : "cz.actis.exevido.admin.CompanyDTO",
           "password" : false,
           "property" : {
             "appliedConstraints" : [ {
               "name" : "nullable",
               "nullable" : true,
               "parameter" : true,
               "propertyName" : "info",
               "valid" : true
             }, {
               "blank" : true,
               "name" : "blank",
               "parameter" : true,
               "propertyName" : "info",
               "valid" : true
             } ],
             "attributes" : { },
             "blank" : true,
             "creditCard" : false,
             "display" : true,
             "editable" : true,
             "email" : false,
             "format" : null,
             "inList" : null,
             "matches" : null,
             "max" : null,
             "maxSize" : null,
             "metaConstraints" : { },
             "min" : null,
             "minSize" : null,
             "notEqual" : null,
             "nullable" : true,
             "order" : 3,
             "owner" : "cz.actis.exevido.admin.CompanyDTO",
             "owningClass" : "cz.actis.exevido.admin.CompanyDTO",
             "password" : false,
             "propertyName" : "info",
             "propertyType" : "java.lang.String",
             "range" : null,
             "scale" : null,
             "size" : null,
             "url" : false,
             "widget" : null
           },
           "propertyName" : "info",
           "propertyType" : "java.lang.String",
           "range" : null,
           "scale" : null,
           "size" : null,
           "url" : false,
           "widget" : null
         },
         "id" : {
           "appliedConstraints" : [ {
             "name" : "nullable",
             "nullable" : true,
             "parameter" : true,
             "propertyName" : "id",
             "valid" : true
           } ],
           "blank" : true,
           "creditCard" : false,
           "display" : true,
           "editable" : true,
           "email" : false,
           "format" : null,
           "inList" : null,
           "matches" : null,
           "max" : null,
           "maxSize" : null,
           "min" : null,
           "minSize" : null,
           "notEqual" : null,
           "nullable" : true,
           "order" : 4,
           "owner" : "cz.actis.exevido.admin.CompanyDTO",
           "password" : false,
           "property" : {
             "appliedConstraints" : [ {
               "name" : "nullable",
               "nullable" : true,
               "parameter" : true,
               "propertyName" : "id",
               "valid" : true
             } ],
             "attributes" : { },
             "blank" : true,
             "creditCard" : false,
             "display" : true,
             "editable" : true,
             "email" : false,
             "format" : null,
             "inList" : null,
             "matches" : null,
             "max" : null,
             "maxSize" : null,
             "metaConstraints" : { },
             "min" : null,
             "minSize" : null,
             "notEqual" : null,
             "nullable" : true,
             "order" : 4,
             "owner" : "cz.actis.exevido.admin.CompanyDTO",
             "owningClass" : "cz.actis.exevido.admin.CompanyDTO",
             "password" : false,
             "propertyName" : "id",
             "propertyType" : "java.lang.String",
             "range" : null,
             "scale" : null,
             "size" : null,
             "url" : false,
             "widget" : null
           },
           "propertyName" : "id",
           "propertyType" : "java.lang.String",
           "range" : null,
           "scale" : null,
           "size" : null,
           "url" : false,
           "widget" : null
         }
       },
       "errors" : {
         "errors" : [ ]
       },
       "id" : "",
       "info" : "",
       "name" : ""
     } ],
     "timestamp" : "2025-07-22T13:04:18.065+02:00"
   }
   
   ```
   </details>
   
   <details>
     <summary>Frontend call response:</summary>
   
   <img width="536" height="502" alt="Image" 
src="https://github.com/user-attachments/assets/4a64d8c9-bd6a-4af5-a8da-76a364826353";
 />
   </details>
   
   ### Steps To Reproduce
   
   I've prepared a very simple example from a Forge generated app, see below. 
Unfortunately I'm unable to replicate the ability to get the "bad" serialized 
dto json there, so above I've attached examples from the project I'm working on.
   
   1. start the application
   2. go to localhost:8080/api/test/ok - the dto is returned as expected
   3. go to localhost:8080/api/test/fail - serialization fails with "Error 
converting Bean with class grails.validation.ConstrainedDelegate - CreditCard 
constraint only applies to a String property"
   
   ### Environment Information
   
   - OS: Manjaro Linux 6.15.6-arch1-1.1-g14
   - JDK: 17.0.15-tem
   
   ### Example Application
   
   https://github.com/pappmiku/grails-7-ser-ex
   
   ### Version
   
   7.0.0-SNAPSHOT


-- 
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: notifications-unsubscr...@grails.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to