Skip to content

Configure ObjectMapper to serialize BigDecimal as String #39302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

Blackbaud-JasonBodnar
Copy link

Configure ObjectMapper to serialize BigDecimal as String so precision and scale are maintained, especially when value ends in 0s (#38691)

Description

As described in #38691, BigDecimal values ending in 0 (ie, 123.40) lose the trailing 0 when saved to the database. When the value is retrieved originalValue.equals(retrivedValue) fails because BigDecimal.equals() takes into account precision and scale and 123.40 does not have the same scale as 123.4.

This is fixed by configure the ObjectMapper to serialize BigDecimal as a String instead of a Number using JsonFormat.Value.forShape(JsonFormat.Shape.STRING).

JsonFormat is only used for serialization so this change is backwards compatible allowing existing values saved as Number to still be deserialized properly.

All SDK Contribution checklist:

  • [ X ] The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • [ X ] I have read the contribution guidelines.

General Guidelines and Best Practices

  • [ X ] Title of the pull request is clear and informative.
  • [ X ] There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • [ X ] Pull request includes test coverage for the included changes.

@github-actions github-actions bot added azure-spring All azure-spring related issues Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. labels Mar 19, 2024
Copy link
Contributor

Thank you for your contribution @Blackbaud-JasonBodnar! We will review the pull request and get back to you soon.

@Blackbaud-JasonBodnar
Copy link
Author

Blackbaud-JasonBodnar commented Mar 19, 2024 via email

Copy link
Member

@FabianMeiswinkel FabianMeiswinkel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Blackbaud-JasonBodnar - thanks for providing this PR - really appreciated. I do not think we can just change the ObjectMapper settings in the ObjectMapperFactory - your argument that this won't break as long as someone uses Java and Jackson is true - but within a workload custoemrs could easily also use other clients/SDKs with different serializers - and changing the json from number to string (while I agree is the best option to deal with the challenges around json numbers in many cases) might not be acceptable for everyone. Since this technically is a breaking change it could not be done by default. I am working right now on a change in azure-cosmos allowing to provide custom serialization logic - I hope to have a PR out later this week or early next week and will make sure I also add integration test coverage for the BigDecimal use case. I think it might make sense to use the same approach. Can we delay making a final decision for this PR until I have this PR out?

@Blackbaud-JasonBodnar
Copy link
Author

@FabianMeiswinkel : Custom serialization is not the solution. If you go to the original bug as a consumer of a database and a high level SDK, I should not have to be concerned or have any knowledge of how my data is serialized. What I get out of a database should equal what I put in without any configuration on my part.

@kushagraThapar
Copy link
Member

@Blackbaud-JasonBodnar - re-opening this thread. Custom serialization functionality has been added to the Cosmos Java SDK and we think it is the best possible way (lesser of all the evils) to solve this issue.
FYI, I also read this thread which talks about the same problem with jackson - FasterXML/jackson-databind#2087. It has some more information on how to unblock this with some other workarounds.
Nevertheless, as @FabianMeiswinkel mentioned above, we don't think just changing the default object mapper of the Cosmos Java SDK library by default is the correct way to go here, can you please take a look at the custom serialization mechanism and see if it solves your use case.

Here are the changes for custom serialization - https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/cosmos/azure-cosmos/CHANGELOG.md#4590-2024-04-27

@Blackbaud-JasonBodnar
Copy link
Author

@kushagraThapar You already have the ability to provide a custom ObjectMapper in azure-spring-data-cosmos. I'm currently using it to work around the BigDecimal issue:

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ObjectMapperConfig {
    @Bean
    @Qualifier(Constants.OBJECT_MAPPER_BEAN_NAME)
    public ObjectMapper cosmosObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper
                .registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule())
                .configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true)
                .configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configOverride(BigDecimal.class).setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
        return objectMapper;
    }
}

The PR you linked to will certainly help people accessing Cosmos via the SDK without Spring. But I don't think either that or the current workaround I'm using address the two issues I have:

  1. As the consumer of a NOSQL database I shouldn't be concerned with how my data is formatted.
  2. When I insert a document into a NOSQL database if I retrieve the document it should equal the document I inserted (except for auto-populated fields such as id, auditable fields, etc).

Requiring a consumer of the SDK to provide a custom object mapper breaks those two issues. And while I understand you don't want to change the default behavior of the existing object mapper, that's what major versions are for -- breaking functionality. Considering that the existing object mapper is broken with respect to BigDecimal it would not be out of the question to have a major version release that changes existing functionality in order to fix something.

@FabianMeiswinkel
Copy link
Member

@Blackbaud-JasonBodnar - if and when we release a new major version, I indeed think that this is something we should take a close look at. But new major versions come with a very high cost for us - all Azure SDKs are required to keep supporting previous major versions for at least 3 years - so, introducing a new major version is the last possible option we would look into - it would need to be necessary to provide very significant added value to justify the maintenance cost for supporting two major version for >3 years. And the last major version has also shown us that most customers are not eager to adopt an SDK with breaking changes. So, supporting BigDecimals out-of-the-box is something we can and will take a look at when we have to make a breaking change to a new major version - but for sure not sufficient motivation to publish a new major version.

Copy link
Contributor

Hi @Blackbaud-JasonBodnar. Thank you for your interest in helping to improve the Azure SDK experience and for your contribution. We've noticed that there hasn't been recent engagement on this pull request. If this is still an active work stream, please let us know by pushing some changes or leaving a comment. Otherwise, we'll close this out in 7 days.

@github-actions github-actions bot added the no-recent-activity There has been no recent activity on this issue. label Jul 12, 2024
@kushagraThapar
Copy link
Member

@Blackbaud-JasonBodnar circling back on this, I am curious if you want to discuss this further or can we close this?

@github-actions github-actions bot removed the no-recent-activity There has been no recent activity on this issue. label Jul 12, 2024
@Blackbaud-JasonBodnar
Copy link
Author

@Blackbaud-JasonBodnar circling back on this, I am curious if you want to discuss this further or can we close this?

I've said what I had to say. We've worked around this bug but I really don't think we should have to. I've given you a PR to fix the issue. Since I don't control the repo this is all I can do unless I want to fork it and go on my own.

@alzimmermsft
Copy link
Member

alzimmermsft commented Jul 17, 2024

Hi @Blackbaud-JasonBodnar, overall, the concept of this change looks good to me but I'd recommend putting this feature behind a configuration flag that is checked at the start of runtime. We cannot be certain how this code is used elsewhere and changing the JSON output from a numeric field to a string field could break other customers. So, making this something that needs to be opted into would be best to retain existing behavior while also being able to fix this issue for others running into this same problem.

Adding some details on to the issue that was filed which prompted this PR. Using ObjectMapper.readTree(String) may opt into using double as the default over BigDecimal due to performance reasons, which leads me to believe that if the JSON string was being converted to a specific object where that JSON field was a BigDecimal you wouldn't be seeing this loss of precision.

https://github.com/FasterXML/jackson-core/blob/2.18/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java#L770
https://github.com/FasterXML/jackson-core/blob/2.18/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java#L988
https://github.com/FasterXML/jackson-databind/blob/2.18/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java#L49

@kushagraThapar
Copy link
Member

Thanks @alzimmermsft and @saragluna for the review and providing further suggestions.

@Blackbaud-JasonBodnar if and when you get some time, can you please add a flag in the application.properties which will allow us to make this an opt-in.
Let us know if you have any questions, thanks for your patience and taking this forward :)

@Blackbaud-JasonBodnar
Copy link
Author

@kushagraThapar : I'll take a crack at making this configurable, but it probably won't happen for at least a week or two.

@kushagraThapar
Copy link
Member

@kushagraThapar : I'll take a crack at making this configurable, but it probably won't happen for at least a week or two.

@Blackbaud-JasonBodnar thanks, I think it works best with our release schedule as well. If you don't get time, let me know and I can take this forward as well, just don't want to overstep on your toes here.

Copy link
Contributor

github-actions bot commented Oct 4, 2024

Hi @Blackbaud-JasonBodnar. Thank you for your interest in helping to improve the Azure SDK experience and for your contribution. We've noticed that there hasn't been recent engagement on this pull request. If this is still an active work stream, please let us know by pushing some changes or leaving a comment. Otherwise, we'll close this out in 7 days.

@github-actions github-actions bot added the no-recent-activity There has been no recent activity on this issue. label Oct 4, 2024
@kushagraThapar
Copy link
Member

@trande4884 can you please take this feature forward?

@github-actions github-actions bot removed the no-recent-activity There has been no recent activity on this issue. label Oct 7, 2024
Copy link
Contributor

Hi @Blackbaud-JasonBodnar. Thank you for your interest in helping to improve the Azure SDK experience and for your contribution. We've noticed that there hasn't been recent engagement on this pull request. If this is still an active work stream, please let us know by pushing some changes or leaving a comment. Otherwise, we'll close this out in 7 days.

@github-actions github-actions bot added the no-recent-activity There has been no recent activity on this issue. label Dec 13, 2024
Copy link
Contributor

Hi @Blackbaud-JasonBodnar. Thank you for your contribution. Since there hasn't been recent engagement, we're going to close this out. Feel free to respond with a comment containing /reopen if you'd like to continue working on these changes. Please be sure to use the command to reopen or remove the no-recent-activity label; otherwise, this is likely to be closed again with the next cleanup pass.

@github-actions github-actions bot closed this Dec 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
azure-spring All azure-spring related issues Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. no-recent-activity There has been no recent activity on this issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants