FasterXML / jackson-databind

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

Allow registering a hook to modify/pre-process String values read #2971

Open cowtowncoder opened 3 years ago

cowtowncoder commented 3 years ago

(note: counterpart to https://github.com/FasterXML/jackson-core/issues/355 -- see more discussion there)

For some use cases -- for example, variable substitution -- it would be beneficial to be able to register a handler that can replace parts of incoming String values before other databinding functionality handles it. Since there are various ways this could occur, I'll add a databinding issue here; the original jackson-core one (see above) may be most likely way to get there, but even if so there probably needs to be a way to register such handler on per-call basis for ObjectReaders (and/or maybe ObjectMapper).

JooHyukKim commented 12 months ago

Example usage suggestion, made it verbose(not lambda) to express that we would need interface ValuePreprocessor also.

class Bean {
    public int hello;
}

// NEW FEATURE
ValuePreprocessor processor = new ValuePreprocessor() {
    @Override
    public String preprocess(String name, String expected) {
           // do something... like trimming
          return 
    }
}

// USAGE
@Test
public test() {
    // polluted input
    String input = a2q("    {`hello`:123}           #_F#EFJ)#")

    // configure it
    ObjectMapper mapper = JsonMapper.builder()
       .registerValuePreprocessor(processor)
       .build();

   // use it, successfully
    assertNotNull(mapper.readValue(input, Bean.class));

   assertThrows(SomeDeserException.class,
       () -> mapper.reader()
                    .withoutValuePreprocessor() // <---- sometimes we may not want it?
                   .readValue(input, Bean.class));
}
pjfanning commented 12 months ago

I'd prefer if the new class was called ValuePreprocessor and it was aimed at allowing values to be modified. So in "name": "value", it would only be allowed to change the value. I think the API in ValuePreprocessor should be public String preprocess(String name, String value). It may also be useful to have a 3rd input parameter, a type descriptor - it would be certainly be useful to know if the value is a number.

I don't know what to do about arrays like "name": [ "value1", "value2" ]. The type descriptor could be an enum of string, number, string-array, number-array. With this example we could have 2 calls to the ValuePreprocessor with the inputs:

If we want to make the names preprocessable, we could add a separate feature to support a NamePreprocessor.

JooHyukKim commented 12 months ago

I'd prefer if the new class was called ValuePreprocessor

Sure, I don't have preference for name yet.

preprocess(String name, String value)

Seems like name would be the JSON key to look for, but how will the parameter value be used?

pjfanning commented 12 months ago

Seems like name would be the JSON key to look for, but how will the parameter value be used?

If you have JSON with this: "name1": "value1"

You get a call to the ValuePreprocessor with this: String preprocess(name1, value1)

The ValuePreprocessor is user provided and the user may have code in their ValuePreprocessor where they want to uppercase values but only when the name value is equal to name1.

JooHyukKim commented 12 months ago

The ValuePreprocessor is user provided and the....

Thank you for the explanation 🙏🏼 I first thought the preprocessor would take in the whole input (like JSON String, .csv or .json file, etc...) and clean up input. But your usage makes sense also. I will go check again the intended usage.

P.S. Modified example usage accordingly.

pjfanning commented 12 months ago

The ValuePreprocessor is user provided and the....

Thank you for the explanation 🙏🏼 I first thought the preprocessor would take in the whole input (like JSON String, .csv or .json file, etc...) and clean up input. But your usage makes sense also. I will go check again the intended usage.

P.S. Modified example usage accordingly.

I could be wrong but I really don't see the point in jackson pre-processing the entire input as one string. A user can trivially do that already in their own code before asking Jackson to parse the modified input. But the ValuePreprocessor as I describe it is useful because the JSON parser has added some value by working out the structure of the input.

JooHyukKim commented 12 months ago

I could be wrong but I really don't see the point in jackson pre-processing the entire input as one string. A user can trivially do that already in their own code before asking Jackson to parse the modified input.

I concur 👍🏼

cowtowncoder commented 12 months ago

Correct: this would apply to String valued (Object) properties, not to whole input document.

But I suspect that

https://github.com/FasterXML/jackson-core/issues/355

is more generally usable approach.