Skip to content

JsonParser#getNumberType when the current token is non-numeric throws an exception instead of returning null #1433

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

Open
CrazySqueak opened this issue May 25, 2025 · 5 comments

Comments

@CrazySqueak
Copy link

Context

I wrote a deserializer for colours which accepts them both as a raw integer (0xRRGGBB) or as a string ("name" or "#RRGGBB").

I assumed that, as mentioned in the javadoc, getNumberType would return a non-null number constant if the current token is a number, and null if it were a string or something else, thus making it suitable for discerning between the two options.

Expected Behaviour

JsonParser#getNumberType returns null for a non-numeric token (in this case, of type VALUE_STRING), as stated by the documentation.

Actual Behaviour

It throws an exception: com.fasterxml.jackson.core.JsonParseException: Current token (VALUE_STRING) not numeric, can not use numeric value accessors.

From what I can see, this is because ParserBase#getNumberType() calls _parseNumericValue to determine what number it's looking at, which always expects the current token to be numeric and throws an exception if it isn't.

Full Traceback

com.fasterxml.jackson.databind.JsonMappingException: Current token (VALUE_STRING) not numeric, can not use numeric value accessors
 at [Source: "{"colour": "#123456"}"; line: 1, column: 13] (through reference chain: my.project.Test2["colour"])
        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._deserializeWithErrorWrapping(BeanDeserializer.java:566)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeUsingPropertyBasedWithUnwrapped(BeanDeserializer.java:848)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithUnwrapped(BeanDeserializer.java:703)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:347)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:564)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:439)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
        at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:4650)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2831)
        at com.fasterxml.jackson.core.JsonParser.readValueAs(JsonParser.java:2277)
        at my.project.Test2$JsonDeserializer.deserialize(<snip>)
        at my.project.Test2$JsonDeserializer.deserialize(<snip>)
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:564)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:439)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
        at my.project.Test2.fromMessage(<snip>)
        ... 5 more
Caused by: com.fasterxml.jackson.core.JsonParseException: Current token (VALUE_STRING) not numeric, can not use numeric value accessors
 at [Source: "{"colour": "#123456"}"; line: 1, column: 13]
        at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2391)
        at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:740)
        at com.fasterxml.jackson.core.base.ParserBase._parseNumericValue(ParserBase.java:835)
        at com.fasterxml.jackson.core.base.ParserBase.getNumberType(ParserBase.java:660)
        at my.project.ColourDeserializer.deserialize(<snip>)
        at my.project.ColourDeserializer.deserialize(<snip>)
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:564)
        ... 35 more

Version

com.fasterxml.jackson.core:jackson-annotations:2.19.0
com.fasterxml.jackson.core:jackson-core:2.19.0
com.fasterxml.jackson.core:jackson-databind:2.19.0 
@pjfanning
Copy link
Member

pjfanning commented May 25, 2025

Changing code behaviour tends to break stuff for other users. I don't think there is a good reason to change the behaviour here. It is perfectly justifiable to throw an exception in getNumberType if the value is not a number.

Even if we did change the behaviour, you would have to wait for a non patch release and this is likely to be many months away. Are you willing to wait multiple months, maybe 6 for the 2.20.0 release?

The Parser API has a currentToken() method that returns a JsonToken enum value that you can check.

@pjfanning
Copy link
Member

You call also try getText which I think will return the number as a string regardless of whether it appears as a number or as a string in the input doc. You can then parse the string.

If you are trying to get Jackson to parse hexadecimal numbers for you, I am not at all sure if Jackson has this built-in. It is not part of the JSON spec.

@CrazySqueak
Copy link
Author

CrazySqueak commented May 25, 2025

I've already found a different method, but I figured that I'd report this so that the behaviour can be documented. I agree that updating the documentation to match the current behaviour is better for backwards compatibility (Hyrum's law and all that).

@pjfanning
Copy link
Member

pjfanning commented May 25, 2025

Thanks @CrazySqueak - I missed the bit about the javadoc. It does indeed say that the method should return null. So maybe this is a bug. There may be a justification to change the behaviour to match the documented behaviour but still this is not likely to appear until 2.20.0.

https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/latest/com/fasterxml/jackson/core/JsonParser.html#getNumberType--

@pjfanning
Copy link
Member

@cowtowncoder I had a look at ParserBase.getNumberType and it looks like to make that method match its Javadoc and return nulls for non-number types that we would have to do something like write a new method to replace _parseNumericValue for this use case. We could copy/paste _parseNumericValue and make the copy not throw an exception for certain token types like VALUE_STRING.

It might be easier just to change the Javadoc to remove the bit about returning nulls for non-number types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants