Overview

org.springframework.util.ObjectUtils.nullSafeToString(Object) exists for generating a toString() representation of various objects in a "null-safe" manner, including support for object graphs, collections, etc.

However, there are times when we would like to generate a more "concise" null-safe toString() representation that does not include an entire object graph (or potentially a collection of object graphs).

org.springframework.beans.BeanUtils.isSimpleValueType(Class<?>) already provides a check for an opinionated list of "simple types"; however, that resides in spring-beans and therefore cannot be used in spring-core.

We should introduce ObjectUtils.nullSafeConciseToString(Object) that generates a concise, null-safe string representation of the supplied object relying on logic similar to BeanUtils.isSimpleValueType() to decide when to use the standard toString() implementation for the supplied object and when to replace the standard implementation with a more concise format -- for example, something along the lines of what Object#toString generates instead of including the toString() representation of an entire object graph recursively.

Deliverables

  • [x] Introduce nullSafeConciseToString(Object) in ObjectUtils

Comment From: gebhardt

This change in the FieldError.toString() method makes our bean validation unusable. If we validate a File or an Optional<String> (e.g Optional<@Pattern(regexp="...") String> the resulting error Message is useless, as it does not contain the Filename or the content of the optional String any more. I highly suggest to rollback the changes.

Comment From: bclozel

@gebhardt This was done in #30661, see above in the linked issues. The release is already out.

Comment From: gebhardt

@bclozel Unfortunately only UUID and neither File nor Optional was added to the classes where the toString() method gets called. I assume that there will be a very long list of classes in the future that are regarded as simple type. I'd suggest to rollback the changes in the Validation output, i.e. call ObjectUtils.nullSafeToString(...) in the org.springframework.validation.FieldError.toString() method.

Comment From: bclozel

Can you create a new issue and provide a sample showing the issue?

Comment From: jhoeller

@gebhardt we're currently considering what to do about this for the upcoming releases in mid July. It would be great if you could create a separate issue for the FieldError.toString() regression for which we would potentially revert to the original behavior, while still using the concise representation for ConversionFailedException and potentially also for BindException.

Could you please clarify how you are consuming the FieldError.toString() output? Are you calling the method directly (like in #30799) or indirectly via Errors/BindingResult/BindException?

Comment From: gebhardt

We are using the FieldError indirectly via Bean Validation in our REST API or BindExeptions when validating configuration files.

We are using Optional fields in our Rest Patch requests, and the validation annotations look like this:

private Optional<@Pattern(regexp="..." String> prop1;
private Optional<@Size(min = 1, max = 255) String> prop2;

Now I get exception messages like this, that do not contain the rejected value in a readable form anymore.

org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument ..  rejected value [java.util.Optional@79004b3f]
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:141)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)

Another example is a configuration file where we have created an @ExistingFile annotation:

@Validated
public class MyConfiguration {

    @ExistingFile
    private File myFile;
}

If I try to initialize an ApplicationContext, I get the following Exception:

Caused by: org.springframework.boot.context.properties.bind.validation.BindValidationException: Binding validation errors on abc.def
   - Field error in object 'abc.def' on field 'myFile': rejected value [java.io.File@566806a]; codes [ExistingFile.abc.def.myFile,ExistingFile.myFile,ExistingFile.java.io.File,ExistingFile]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [abc.def.plugins[0].myFile,myFile]; arguments []; default message [myFile]]; default message [must be an exiting file]; origin "abc.def.myFile" from property source "class path resource [config/my.properties]"

However, if we use the Spring Boot FailureAnalyzer I get a meaningful output:

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'abc.def' to com.exmaple.MyConfiguration failed:

    Property: com.exmaple.myFile
    Value: "/path/to/my/file.txt"
    Origin: " com.exmaple.myFile" from property source "URL [file:/path/to/my.properties]"
    Reason: must be an exiting file.

Comment From: sbrannen

Update

Changes made to FieldError#toString will be reverted in:

  • 30799

Improvements to ObjectUtils.nullSafeConciseToString are being made in:

  • 30805

  • 30810