Giter Site home page Giter Site logo

jackson-datatype-money's People

Contributors

aboivin avatar alexanderyastrebov avatar bocytko avatar bountin avatar darioseidl avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar geoand avatar github-actions[bot] avatar happyherp avatar jhorstmann avatar lappleapple avatar lukasniemeier-zalando avatar msparer avatar toukovk avatar whiskeysierra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jackson-datatype-money's Issues

MonetaryAmount serialization

Hello,

I have an issue during the serialization of a MonetaryAmount.

When I try to serialize an amount of 100.00 EUR for example, the corresponding JSON is the following :
{"amount":"1E+2","currency":"EUR"}

I would like to get {"amount":"100.00","currency":"EUR"} instead.

In fact, what I expect is an amount formatted with the currency digit places number. If the currency has 3 digit places (like the TND) I want to have {"amount":"100.000","currency":"TND"}.

Maybe I missed something in the library that's doing this.

Thank you in advance.

Custom naming for currency and amount fields

Would it be possible to make the names of the MonetaryAmount configurable? So for example a monetary amount would be serialized like this if you configure the amount field to be named value:

{ "currency" : "EUR", "value" : 100 }

Ability to change default digits for a particular currency

Ability to change default digits for a particular currency

Detailed Description

For eg default digits for TWD currency is 2 but we do need to return zero digits

Context

Accordiing to busineess needs users can change the decimal points for specific currency

Strange behaviour in case of 100.00 EUR value

Hi all,

I´m wondering why this test runs into an error:

@Test
    public void shouldSerializeWithoutFormattedValueIfFactoryProducesNull() throws JsonProcessingException {
        final ObjectMapper unit = new ObjectMapper()
                .registerModule(new MoneyModule());

        final String expected = "{\"amount\":100.00,\"currency\":\"EUR\"}";
        final String actual = unit.writeValueAsString(Money.of(100.00, "EUR"));

        assertThat(actual, is(expected));
    }

Result:

java.lang.AssertionError: 
Expected: is "{\"amount\":100.00,\"currency\":\"EUR\"}"
     but: was "{\"amount\":1E+2,\"currency\":\"EUR\"}"
Expected :{\"amount\":100.00,\"currency\":\"EUR\"}
Actual   :{\"amount\":1E+2,\"currency\":\"EUR\"}

CurrencyUnitDeserializer throws UnknownCurrencyException if empty string is passed

Hello I have a rest service and I want to set my currency to null. That's why I do a POST request specifying that {currency:} and on the server side I get the following exception:

Caused by: UnknownCurrencyException [currencyCode=]
	at javax.money.spi.MonetaryCurrenciesSingletonSpi.getCurrency(MonetaryCurrenciesSingletonSpi.java:74)
	at javax.money.Monetary.getCurrency(Monetary.java:422)
	at org.zalando.jackson.datatype.money.CurrencyUnitDeserializer.deserialize(CurrencyUnitDeserializer.java:25)
	at org.zalando.jackson.datatype.money.CurrencyUnitDeserializer.deserialize(CurrencyUnitDeserializer.java:12)

Looking at the CurrencyUnitDeserializer it looks like if an empty string is passed then the given exception will be raised:

    @Override
    public CurrencyUnit deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {
        final String currencyCode = parser.getValueAsString();
        return Monetary.getCurrency(currencyCode);
    }

Can you please add a empty-string check so that if the value given is null or empty string then the method returns null.

Type Id Handling not supported

Hi Folks,

I want to serialise Money types with Type Info, so that I can properly use it in serialised collections. In order to achieve this, I use Jackson DefaultTyping for polymorphic serialisation. Otherwise, type information will get lost when I serialise a Collection.

This is my (simplified) scenario:

public class ParentDomainObject {
  private Long id;
  private Set<ChildDomainObject> children;
}

public class ChildDomainObject {
  private Long id;
  private MonetaryAmount cost;
}

public class SomeHandler {
  public void serialiseMyDomainObjects() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new MoneyModule());
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);

    ParentDomainObject o1 = new ParentDomainObject();
    // add some child objects

    ParentDomainObject o2 = new ParentDomainObject();
    // add some child objects

    mapper.writeValueAsString(Sets.asSet(o1, o2));
  }
}

When I try to run this, I get the following Exception:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type org.javamoney.moneta.Money (by serializer of type org.zalando.jackson.datatype.money.MonetaryAmountSerializer)

I hope you get the point. Is this simply not supported (at the moment) or is there any way to work around this?

Cheers,
Stefan

Create LocaleProvider to resolve correctly current locale

Hello,
the MonetaryAmountSerlializer currently always gets the default locale from the configuration (mine is en_US ):
https://github.com/zalando/jackson-datatype-money/blob/master/src/main/java/org/zalando/jackson/datatype/money/MonetaryAmountSerializer.java#L57

The problem I have is that my customers are able to change the session currency they view the website in.

I'd like to introduce a new type (something like LocaleProvider) which would be injected in the constructor of the MonetaryAmountSerializer together with the MonetaryAmountFormatFactory like this:

    public MonetaryAmountSerializer(final MonetaryAmountFormatFactory factory, final LocaleProvider provider) {
        this.factory = factory;
        this.localeProvider = localeProvider;
    }

Of course you provide a default implementation which simple takes it from the configuration (the current behavior). But this way I would be able to substitute the default behaviour with my own too.

Thanks and keep up the good work :)

Provide number value serializer factory

As a library
I want to provide number value serializer factory
So that I can hide implementation types
And have more freedom refactoring them

DecimalNumberValueSerializer and QuotedDecimalNumberValueSerializer are to be replaced by proposed factory.

CurrencyUnitDeserializer deserializes invalid currencies

Hello,

at the moment the CurrencyUnitDeserializer will deserialize anything you pass as a currency. For example strings like 1123, eur, or xyz are all invalid but the CurrencyUnitDeserializer will create a currency unit out of it. I think the correct behaviour is to only deserialize valid strings, such as EUR or USD. This could be done with the following code:

    @Override
    public CurrencyUnit deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {
        return Monetary.getCurrency(parser.getValueAsString());
    }

Concrete RI type Money not accepted for serialization

Serializing a class that uses the reference implementation Money class instead of javax.money.MonetaryAmount will fail with the following message:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class org.javamoney.moneta.Money]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)

Example class:

import org.javamoney.moneta.Money;

public class MyValueObject {
    public Money price;
    public String name;
    public MyValueObject() {
    }
    public MyValueObject(Money money, String name) {
        this.price = money;
        this.name = name;
    }
}

If you use MonetaryAmount instead, everything works fine.

I would expect that either the docs mention that you cannot use RI-Types in your objects, or make the Serializer check for classes that implement MonetaryAmount.

See example JUnit test project here: test-money-json.zip

CNH currency is not being deserialized

Description

com.fasterxml.jackson.databind.JsonMappingException: Unknown currency code: CNH

Expected Behavior

Handling CNH currency by lib

Actual Behavior

com.fasterxml.jackson.databind.JsonMappingException: Unknown currency code: CNH

Possible Fix

Extend list of currencies.

Steps to Reproduce

  1. Run junit test with CNH currency.

Hide de/serializer implementations

Right now all de/serializer implementations are public so client could use them directly, if needed. We should consider making them package private and only open them up if the need arises. It would reduce our public API surface drastically.

Unwanted transitive dependency to jdk8 version of money api and ri

This library should be compatible with both the jdk8 and jdk7-backport versions of java money, that's why we moved the dependencies into separate profiles. Unfortunately this does not seem to work, as this output from mvn dependency:tree from a project shows:

[INFO] +- org.zalando:jackson-datatype-money:jar:0.6.0:compile
[INFO] |  +- javax.money:money-api:jar:1.0:compile
[INFO] |  \- org.javamoney:moneta:jar:1.0:compile

Looking at the deployed pom, there is no direct dependency outside of profiles to api or ri, and I did not think that mvn would activate profiles inside a dependency.

Add support for RoundedMoney

Right now there are factories for deserialization support for

  • FastMoney
  • Money

For completeness and consistency there should also be support for RoundedMoney.

RestTemplate Integration

Is there some additional configuration required to get deserialization working with Spring's RestTemplate?

I'm getting the following error:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not construct instance of javax.money.MonetaryAmount: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of javax.money.MonetaryAmount: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: java.io.PushbackInputStream@2065cbae; line: 2, column: 13] (through reference chain: com.mypackage.PriceResource["price"])

I'm using version org.zalando:jackson-datatype-money:1.0.0-RC2 with 'org.javamoney:moneta:1.1' and am trying to deserialize:

{
    "price": {
        "amount": 1799.99,
        "currency": "USD"
    }
}

into:

import javax.money.MonetaryAmount;
import org.springframework.hateoas.ResourceSupport;

public class PriceResource extends ResourceSupport {
  private MonetaryAmount price;
  public PriceResource() {}
  public MonetaryAmount getPrice() {
    return price;
  }
  public void setPrice(MonetaryAmount price) {
    this.price = price;
  }
}

Using a RestTemplate built as follows:

RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
restTemplate.getMessageConverters().removeIf(messageConverter -> messageConverter.getClass() == MappingJackson2HttpMessageConverter.class);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter(objectMapper));

where the objectMapper is created by SpringBoot (v1.5.7) and injected into the previous method and supplied the MoneyModule via the following:

@Configuration
class JacksonConfig {
  @Bean
  public MoneyModule moneyModule() {
    return new MoneyModule();
  }
}

To show that the RestTemplate has an ObjectMapper with the MoneyModule, here's the instance view in the debugger right after catching the above exception:

billingserviceclient_java_-_expressplay_services_-____dev_repos_exp-services_bitbucket_

Note that I've also tried both of the following configs, as the README.md wasn't clear if was necessary or not, however both generated the same error as above:

@Configuration
class JacksonConfig {
  @Bean
  public MoneyModule moneyModule() {
    return new MoneyModule().withMoney();
  }
}
@Configuration
class JacksonConfig {
  @Bean
  public MoneyModule moneyModule() {
    return new MoneyModule().withMonetaryAmount(Money::of);
  }
}

MoneyModule seems configured but Jackson not deserializing

If objectMapper.getRegisteredModuleIds() contains org.zalando.jackson.datatype.money.MoneyModule, why would this error still occur?

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `javax.money.MonetaryAmount` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

It seems the Money Jackson HttpMessageConverter not configured? Is there another setup step I missed, e.g. also need to configure the converter?

Running with Spring Boot v2.3.1.RELEASE, Spring v5.2.7.RELEASE.

Configured by this in a @configuration class:

    @Bean
    public Module moneyModule()
    {
        return new MoneyModule();
    }

Added a link from main jackson page

(apologies for adding a "non-issue" issue, but figured this is a reasonable way to contact authors -- will be happy to communicate using better methods too afterwards -- you can contact me via tatu at fasterxml dot com for example, in addition to referencing via my account)

First of all, thank you for writing this datatype module. I think it is great to get serialization support for new standard Java APIs from get go.

Second: I hope you don't mind my adding a link on https://github.com/FasterXML/jackson to point to this project. I mostly mention this because sometimes authors wish to reach certain version before announcing or something; if so, just let me know. Ditto for any changes to verbiage of the link; will be happy to update it any way you want, including mention of sponsors for work or such (we like to give credit to companies that help OSS development).

Known issue with setters on MonetaryAmount

Hi,
There is a known issue but no known workaround.
tried Jackson 2.9.5, 2.9.4, and some else..

Caused by: java.lang.IllegalArgumentException: Conflicting setter definitions for property "number": javax.money.MonetaryAmountFactory#setNumber(1 params) vs javax.money.MonetaryAmountFactory#setNumber(1 params)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder.getSetter(POJOPropertyBuilder.java:497)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._renameUsing(POJOPropertiesCollector.java:848)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:336)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:287)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:170)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:164)
        at deployment.test-1.0.0-SNAPSHOT.war//com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:239)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:468)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:90)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:560)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:90)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:560)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:90)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:361)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:90)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:560)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:90)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.core.converter.ModelConverters.resolveAsResolvedSchema(ModelConverters.java:112)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.Reader.parseMethod(Reader.java:943)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.Reader.parseMethod(Reader.java:748)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.Reader.read(Reader.java:422)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.Reader.read(Reader.java:169)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.Reader.read(Reader.java:196)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.oas.integration.GenericOpenApiContext.read(GenericOpenApiContext.java:347)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource.getOpenApi(BaseOpenApiResource.java:49)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.integration.resources.OpenApiResource.getOpenApi(OpenApiResource.java:32)
        at deployment.test-1.0.0-SNAPSHOT.war//io.swagger.v3.jaxrs2.integration.resources.OpenApiResource$Proxy$_$$_WeldClientProxy.getOpenApi(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:564)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:140)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:509)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:400)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:364)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:359)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:366)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:338)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:311)
        at org.jboss.resteasy.resteasy-jaxrs//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:437)
        ... 64 more

It is super annoying as the whole project could've be completely automated but with this all MonetaryAmounts have be treated specially. Please advise.

Suggestion: Change default amount serialization to string

Note: This is likely to be considered a breaking change, but I wanted to raise it due to the progress in #224.

While "amounts" are semantically numeric values, and JSON number values are technically arbitrary-precision decimal, the default handling for JSON numbers in most languages is as a double-precision-similar type (particularly JavaScript Number).

Representing the amount as a decimal number in string format is the safer choice as it eliminates the possibility of silent lossy conversion. With the proposed inclusion of this module into the "official contrib" space (and particularly the tendency to use findAndRegisterModules()), I suggest that the default behavior be changed to use the string representation, with the number representation available in the same optional manner as string currently is.

Cannot deserialize JSON money object inside of Spring Boot @RequestBody

I'm trying to deserialize JSON money object in POST HTTP request to Moneta Money object. Application built on Kotlin + Spring Boot.

Description

Controller class:

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.bind.annotation.RestController

@RestController
final class AddCandidateController() {
    @PostMapping("/addCandidate")
    @ResponseBody
    fun invoke(
        @RequestBody request: AddCandidateRequest,
    ): ResponseEntity<String> {
        println(request.params.salary)

        kotlin.system.exitProcess(1)
    }
}

Request class:

import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import org.javamoney.moneta.Money
import javax.money.MonetaryAmount

data class AddCandidateRequest(
    val params: AddCandidateRequestParams,
)

data class AddCandidateRequestParams(
    val name: String,
    @JsonDeserialize(`as` = Money::class)
    val salary: MonetaryAmount?,
)

JSON Request:

{
  "params": {
    "name": "Test name",
    "salary": {
      "amount": 100.0,
      "currency": "USD"
    }
  }
}

Expected Behavior

In controller request.params.salary contains instantiated Money object with values from the request: Money.of(100.0, "USD").

Actual Behavior

Application error:

2024-08-10T15:06:26.420+03:00 ERROR 35933 --- [monn] [nio-8090-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.javamoney.moneta.Money]] with root cause

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.javamoney.moneta.Money` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 209] (through reference chain: com.example.monn.http.AddCandidateRequest["params"]->com.example.monn.http.AddCandidateRequestParams["salary"])

Context

  1. Serialization works as expected.

  2. Calling deserializer explicitly in controller works well:

val unit = ObjectMapper().findAndRegisterModules()
val content = "{\"amount\":29.95,\"currency\":\"EUR\"}"
val amount = unit.readValue(content, MonetaryAmount::class.java)
  1. I've tried to reconfigure Jackson globally, but it doesn't help:
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.zalando.jackson.datatype.money.MoneyModule

@Configuration
class JacksonConfig {
    @Bean
    fun objectMapper(): ObjectMapper {
        return ObjectMapper().apply {
            registerModule(MoneyModule())
        }
    }
}
  1. I was wanted to manually define MonetaryAmountDeserializer deserializer in request class
data class AddCandidateRequestParams(
    val name: String,
    @JsonDeserialize(using = org.zalando.jackson.datatype.money.MonetaryAmountDeserializer::class)
    val salary: MonetaryAmount?,
)

Receive error:

Cannot access 'MonetaryAmountDeserializer': it is package-private in 'org.zalando.jackson.datatype.money'

  1. Without JsonDeserialize annotation in request:
data class AddCandidateRequestParams(
    val name: String,
    val salary: MonetaryAmount?,
)

It returns error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of javax.money.MonetaryAmount (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

Your Environment

  • com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2
  • org.javamoney:moneta:1.4.4
  • org.jetbrains.kotlin:kotlin-stdlib:1.9.25
  • org.springframework.boot:spring-boot-starter-web:3.3.2
  • org.zalando:jackson-datatype-money:1.3.0

can I provide a custom MonetaryAmountDeserializer?

Could not find how could I provide a custom MonetaryAmountDeserializer
I would need it handle cases when amount or currency are not provided. In this case setting the MonetaryAmount field to null in the response object.

Or is there any other way to do this?

Failed to get nested archive for entry BOOT-INF/lib/moneta-1.4.2.pom after upgrading to 1.2.0

Description

I am working on a spring boot project and upgrading the jackson-datatype-money to 1.2.0 which causes the jar file is failed to unpacked because the pom file is included in the jar for this version.

Expected Behavior

It should work as same as 1.1.0 which is not included the pom file.

Actual Behavior

I think it's probably why it ends up including the pom file
https://github.com/zalando/jackson-datatype-money/blob/main/pom.xml#L86
then causes the issue that I have here:

Exception in thread "main" java.lang.IllegalStateException: Failed to get nested archive for entry BOOT-INF/lib/moneta-1.4.2.pom
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:108)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:86)
        at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:70)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.io.IOException: Unable to open nested jar file 'BOOT-INF/lib/moneta-1.4.2.pom'
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:256)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:241)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:103)
        ... 4 more
Caused by: java.io.IOException: Unable to find ZIP central directory records after reading 17305 bytes
        at org.springframework.boot.loader.jar.CentralDirectoryEndRecord.<init>(CentralDirectoryEndRecord.java:65)
        at org.springframework.boot.loader.jar.CentralDirectoryParser.parse(CentralDirectoryParser.java:52)
        at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:123)
        at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:111)
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:289)
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:264)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:252)

Possible Fix

Steps to Reproduce

  1. upgrade the jackson-datatype-money from 1.1.0 to 1.2.0

Context

Your Environment

  • Version used: Spring Boot 2.1.3.RELEASE.

Integration with JSR-303 and Spring Validation

I'd like to use Spring Validation all across my application. However, default deserializer kicks in before Spring Validation and JsonParseException is thrown. Is there any way that doesn't require writing custom deserializer to supress default check if fields are present?

Amount is sent as number, consider string instead

I wanted to find another page which outlines in more depth the issues, but I could only find the ones below.

A quick summary is some JSON parsers will deserialize numbers as floats which obviously is problematic for Money. Other issues with eventually being deserialized as a float are limitations on size and problems with adding/subtracting. If you send it as a String it will be deserialized as a String.

Various browsers use IEEE-754 to represent their numbers by default which would cause issues.

http://stackoverflow.com/questions/30249406/what-is-the-standard-for-formatting-currency-values-in-json

nlohmann/json#98

shopspring/decimal#21

Adjust No-Args Constructor in CurrencyUnitSerializer to be public?

Is there any chance we could change the no-args constructor of CurrencyUnitSerializer to have public visibility?

Not sure if there's any particular reason for it to be package-private, but currently it's not possible to use this serializer in a SimpleModule (i.e., without using the MoneyModule or some other class in the org.zalando.jackson.datastype.money package).

MonetaryAmountDeserializer currency

I'm having trouble with the MonetaryAmountDeserializer.

When jackson attempts to create a new MoneyNode, it fails in calling the constructor because CurrencyUnit is an interface and it doesn't know what concrete type to use.

The fix is easy - change the currency to a string and then convert internally:

        @JsonCreator
    MoneyNode(@JsonProperty("amount") final BigDecimal amount,
              @JsonProperty("currency") final String currency) {
        this.amount = amount;
        this.currency = CurrencyUnitBuilder.of(currency, "default").build();
    }

SpringFox Generation

We've found this library to work great parsing and rendering MonetaryAmount instances as:

{
    "amount": 900,
    "currency": "USD"
}

However when SpringFox generates the Swagger docs on the same code, it describes the rendering as:

{
    "context": {
        "empty": true,
        "fixedScale": true,
        "maxScale": 0,
        "precision": 0,
        "providerName": "string"
    },
    "currency": {
        "context": {
            "empty": true,
            "providerName": "string"
        },
        "currencyCode": "string",
        "defaultFractionDigits": 0,
        "numericCode": 0
    }
}

Is there a way to solve this?

Serialization fails after upgrade to Spring Boot 2.3.5 / Jackson 2.11.3

After upgrading from Spring Boot 2.3.4 to Spring Boot 2.3.5, which upgrades Jackson from 2.11.2 to 2.11.3, serialization of Money with the MonetaryAmountSerializer fails with the following error:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Can not start an object, expecting field name (context: Object); nested exception is com.fasterxml.jackson.core.JsonGenerationException: Can not start an object, expecting field name (context: Object)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:351)
	at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104)
	at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:277)
	at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:219)
	at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
	at org.springframework.hateoas.server.mvc.RepresentationModelProcessorHandlerMethodReturnValueHandler.handleReturnValue(RepresentationModelProcessorHandlerMethodReturnValueHandler.java:134)
	at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:123)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at at.rechnerherz.busangebot.config.logback.MdcFilter.doFilter(MdcFilter.kt:32)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:239)
	at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:215)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:157)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
	at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:747)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: com.fasterxml.jackson.core.JsonGenerationException: Can not start an object, expecting field name (context: Object)
	at com.fasterxml.jackson.core.JsonGenerator._reportError(JsonGenerator.java:2151)
	at com.fasterxml.jackson.core.json.JsonGeneratorImpl._reportCantWriteValueExpectName(JsonGeneratorImpl.java:233)
	at com.fasterxml.jackson.core.json.JsonGeneratorImpl._verifyPrettyValueWrite(JsonGeneratorImpl.java:223)
	at com.fasterxml.jackson.core.json.UTF8JsonGenerator._verifyValueWrite(UTF8JsonGenerator.java:1132)
	at com.fasterxml.jackson.core.json.UTF8JsonGenerator.writeStartObject(UTF8JsonGenerator.java:363)
	at org.zalando.jackson.datatype.money.MonetaryAmountSerializer.serialize(MonetaryAmountSerializer.java:73)
	at org.zalando.jackson.datatype.money.MonetaryAmountSerializer.serialize(MonetaryAmountSerializer.java:19)
	at org.springframework.hateoas.EntityModel$MapSuppressingUnwrappingSerializer.serialize(EntityModel.java:218)
	at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter.serializeAsField(UnwrappingBeanPropertyWriter.java:127)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:1119)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:207)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:154)
	at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:1119)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$NestedEntitySerializer.serialize(PersistentEntityJackson2Module.java:375)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)
	at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer.serialize(UnwrappingBeanSerializer.java:134)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$ProjectionResourceContentSerializer.serialize(PersistentEntityJackson2Module.java:757)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$ProjectionResourceContentSerializer.serialize(PersistentEntityJackson2Module.java:730)
	at org.springframework.hateoas.EntityModel$MapSuppressingUnwrappingSerializer.serialize(EntityModel.java:218)
	at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter.serializeAsField(UnwrappingBeanPropertyWriter.java:127)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:1119)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:194)
	at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:154)
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
	at com.fasterxml.jackson.datatype.hibernate5.PersistentCollectionSerializer.serialize(PersistentCollectionSerializer.java:243)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:726)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:681)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:637)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)
	at com.fasterxml.jackson.datatype.hibernate5.PersistentCollectionSerializer.serialize(PersistentCollectionSerializer.java:243)
	at org.springframework.hateoas.mediatype.hal.Jackson2HalModule$HalResourcesSerializer.serialize(Jackson2HalModule.java:351)
	at org.springframework.hateoas.mediatype.hal.Jackson2HalModule$HalResourcesSerializer.serialize(Jackson2HalModule.java:303)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
	at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1516)
	at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1006)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:342)
	... 107 common frames omitted

Description

I have yet to investigate this issue further and find out why and where is fails exactly. I'll update this issue as soon as I find anything specific. I suspect it has to do with the upgrade to Jackson 2.11.3. The problem for us is that we need Spring Boot 2.3.5 for another unrelated fix, but that comes with Jackson 2.11.3. We're using jackson-datatype-money 1.1.1. I've tried upgrading to 1.2.2, but that didn't help.

Has anybody encountered the same problem?

Make deserialization working without jackson-module-parameter-names

Currently it seems it is not possible de deserialize data without having jackson-module-parameter-names dependency and registering it in Jackson ObjectMapper. It would be nice to avoid this by adding @JsonCreator and @JsonProperty("foo") annotations to classes like MoneyNode.

Support @JsonUnwrapped please

As far as I understood, @JsonUnwrapped does not work for MonetaryAmount type field

Detailed Description

DTO class

public class goods {
    String description;
    @JsonUnwrapped
    MonetaryAmount price;
}

does not serialize as

{
"description": "table",
"amount": 1000,
"currency": "USD"
}

Context

Possible Implementation

Your Environment

  • Version used: 1.3.0

Unable to parse CSV data

There seems to be no path to using this module together with jackson-dataformat-csv.

Description

When defining a column as MonetaryAmount and attempting to deserialize, the following error appears:

Exception in thread "main" java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Missing property: 'amount'
 at [Source: (StringReader); line: 1, column: 608] (through reference chain: com.company.bank.convert.model.CsvMt940TransAction["Betrag"])
        at com.company.bank.convert.CsvImporter.deserialize(CsvImporter.java:121)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at one.util.streamex.PairSpliterator$PSOfRef.lambda$forEachRemaining$5(PairSpliterator.java:247)
        at java.base/java.nio.file.FileChannelLinesSpliterator.forEachRemaining(FileChannelLinesSpliterator.java:117)
        at one.util.streamex.PairSpliterator$PSOfRef.forEachRemaining(PairSpliterator.java:242)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
        at one.util.streamex.AbstractStreamEx.forEach(AbstractStreamEx.java:360)
        at com.company.bank.convert.CsvImporter.tryParse(CsvImporter.java:90)
        at com.company.bank.convert.App.main(App.java:45)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Missing property: 'amount'
 at [Source: (StringReader); line: 1, column: 608] (through reference chain: com.company.bank.convert.model.CsvMt940TransAction["Betrag"])
        at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392)
        at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1821)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:315)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
        at com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:283)
        at com.fasterxml.jackson.databind.MappingIterator.readAll(MappingIterator.java:323)
        at com.fasterxml.jackson.databind.MappingIterator.readAll(MappingIterator.java:309)
        at com.company.bank.convert.CsvImporter.deserialize(CsvImporter.java:116)
        ... 13 more
Caused by: com.fasterxml.jackson.core.JsonParseException: Missing property: 'amount'
 at [Source: (StringReader); line: 1, column: 608]
        at org.zalando.jackson.datatype.money.MonetaryAmountDeserializer.checkPresent(MonetaryAmountDeserializer.java:73)
        at org.zalando.jackson.datatype.money.MonetaryAmountDeserializer.deserialize(MonetaryAmountDeserializer.java:64)
        at org.zalando.jackson.datatype.money.MonetaryAmountDeserializer.deserialize(MonetaryAmountDeserializer.java:21)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
        ... 18 more

Expected Behavior

I expected it to work.

Actual Behavior

I get the error pasted above.

Possible Fix

Unlike JSON, CSV does not support nested properties, which I suspect interferes with the idea of how jackson-dataformat-csv works by default. But it should still be possible to parse a monetary value; currency could be set on the mapper instead of taking it from the field.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.