Skip to content

JsonParser#getNumberType() throws JsonParseException when the current token is non-numeric 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

Closed
CrazySqueak opened this issue May 25, 2025 · 7 comments
Labels
documentation Issue that documentation improvements could solve or alleviate

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.

@cowtowncoder
Copy link
Member

I'll need to dig in to this bit more later tonight, but my first instinct is to agree with you and @CrazySqueak that we'll probably better change documentation -- esp. for 2.x.

For 3.x we could change behavior too, but not 100% sure which behavior I prefer TBH.

@cowtowncoder cowtowncoder added the documentation Issue that documentation improvements could solve or alleviate label May 29, 2025
@cowtowncoder
Copy link
Member

Will change Javadocs and also add testing; created #1434 for follow-up work for 3.0 to actually change behavior.

@cowtowncoder cowtowncoder changed the title JsonParser#getNumberType when the current token is non-numeric throws an exception instead of returning null JsonParser#getNumberType() throws JsonParseException when the current token is non-numeric instead of returning null May 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Issue that documentation improvements could solve or alleviate
Projects
None yet
Development

No branches or pull requests

3 participants