FasterXML / jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)
Apache License 2.0
3.53k stars 1.38k forks source link

Proposal: Add Nullable Conversion Methods (e.g., asNullableInt) for JsonNode #4802

Open JinseongHwang opened 5 days ago

JinseongHwang commented 5 days ago

Is your feature request related to a problem? Please describe.

Hi there,

It doesn't seem to provide the ability to convert a JsonNode to a nullable int(Integer). In JsonNode.java, asInt() returns an int type and if there is no value, it returns 0 as the default value.

Describe the solution you'd like

I think it would be nice to have a method like asNullableInt() that returns an Integer that is a nullable wrapper class. The same goes for Long, Double, and Boolean.

If you agree with me, I'd like to contribute to this project.

Usage example

I'm using it like this. (However, this is just an example, and it would be possible to pass the nullable option as a parameter.)

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.node.ObjectNode

class FooDeserializer : JsonDeserializer<Foo>() {
    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Foo {
        val node = p.codec.readTree<ObjectNode>(p)
        val someIntValue = node.get("some_int_value")?.asNullableInt()
        return Foo(someIntValue)
    }

    private fun JsonNode.asNullableInt(): Int? { // I wish this was a feature inside jackson-databind.
        return if (this.isNull) null else this.asInt()
    }
}

Additional context

No response

JooHyukKim commented 5 days ago

There's no such method yet? Then makes sense wrt usage. But I'm a bit concerned about the naming though.

'int' can never be nullable in Java, only Kotlin. 'Integer' can be nullable in Java. We might want to check Scala as well.

pjfanning commented 5 days ago

java.lang.Integer is the best return type - works for Java, Kotlin and Scala.

I think the changes could end up being very large because we would probably need a canConvertToNullableInt method too (as well as for other number types and boolean). canConvertToNullableInt would return true if the underlying number data was a valid int but also true if the data is null.

JooHyukKim commented 5 days ago

Now that you mention it PJ, if we were to implement, is it possible that we might end up being quite large, for Float or Double and such🤔?

The rationale behind it could be "why just only integer? Why not double or float?"

JinseongHwang commented 5 days ago

@JooHyukKim I think Long, Double, Boolean, etc. should be implemented the same way. 👍 I wrote in the 'Describe the solution you'd like' part of the body above.

JooHyukKim commented 5 days ago

I wrote in the 'Describe the solution you'd like' part of the body above.

Yup, I read that. Thank you 🙏🏼. The feature itself seems reasonable.

JooHyukKim commented 5 days ago

I think Long, Double, Boolean, etc. should be implemented the same way. 👍

I guess I was trying to reach this point of view. I agree that we most probably would want to have all wrapper types implementations be provided, in same version. Which immedately leads to the question of which version? Because like @pjfanning said above this could become quite large, eating up a whole new minor version.

Having said that, honestly, we could consider this set of feature here as a way to attract users to adopt Jackson 3.0 more if we choose 3.0. Let's hear what @cowtowncoder has in mind wrt version & milestones, and also timing.

cowtowncoder commented 3 days ago

Ok, first things first; there are/were some plans to further extend methods:

https://github.com/FasterXML/jackson-future-ideas/wiki/JSTEP-3

now on "nullable"... that's one possibility, although use of Optional would be another one (we can use that with Java 8 baseline).

Naming-wise for Integer we could actually add asInteger(), (instead of asInt()), but that couldn't be done with Long or Double.

But also keep in mind there's intValue() vs asInt(). Former only coerces numeric values; latter will also support converting JSON Strings that can be parsed as ints.

So we need to be clear on exact expected semantics and naming.

I guess what I am saying is that:

  1. Yes, I think we could use a limited set of new accessors to support use of marker (either null or Optional) to indicated node is not of expected type
  2. But I am not 100% of asNullableInt[Eger] as choice (naming, or semantics)

It might help if @JinseongHwang could extend a little bit on kind of actual usage.

JinseongHwang commented 1 day ago

Thank you for your feedback 👍 I'll think about it and share it with you asap