Skip to content
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

Allow/support multiple @JsonCreator in single class #2353

Closed
babochenko opened this issue Jun 13, 2019 · 8 comments
Closed

Allow/support multiple @JsonCreator in single class #2353

babochenko opened this issue Jun 13, 2019 · 8 comments

Comments

@babochenko
Copy link

babochenko commented Jun 13, 2019

I have an idea which I'd like to implement in this module. BUT, I want to hear other Jackson users' opinions on this idea first


Imagine I have a following class:

class SuperToken {
    long time;
    String username;
    byte[] password;
}

So I'd like to deserialize it via dynamic deserializer selection:

What i want to do:

class SuperToken {
    long time;
    String username;
    byte[] password;
    
    @JsonCreator(mode=DELEGATING) // invoked when a string is passed
    public static SuperToken from(String username) {
        SuperToken token = new SuperToken();
        token.username = username;
        token.password = new byte[];
        token.time = System.currentTimeMillis();
        
        return token;
    }
    
    @JsonCreator(mode=PROPERTIES) // invoked when an object is passed, pre-validating property existence
    public static SuperToken create(
            String username,
            byte[] password,
            long time) {
        SuperToken token = new SuperToken();
        token.username = username;
        token.password = new byte[];
        token.time = time;

        return token;
    }
}

Unfortunately, this is not possible right now (as explained
once on Stackoverflow)
and then in your Github issues)

What i HAVE to do (now):

class SuperToken {
    long time;
    String username;
    byte[] password;
    
    @JsonCreator(mode=PROPERTIES)
    public static SuperToken create(
            JsonNode node) {
        if (node.isTextual()) {
            String username = node.asText();
            return ...
        } else {
            // all this <strong>BORING</strong> deserialization
        }
    }
}
@cowtowncoder cowtowncoder changed the title Handle multiple @JsonCreator in single class Allow/support multiple @JsonCreator in single class Sep 11, 2019
@kirillgroshkov
Copy link

We are also searching for a way to achieve that

@cowtowncoder cowtowncoder added 2.11 and removed 2.10 labels Nov 26, 2019
cowtowncoder added a commit that referenced this issue Nov 27, 2019
@cowtowncoder
Copy link
Member

@stasmihailov @kirillgroshkov It should already work, actually, as long as delegating case is scalar-valued (int/long/String/boolean). I added a test in 2.10 branch to verify its functioning.
The only change to test case is to add @JsonProperty for parameters of properties-case (or use Java 8 parameter names module).

@babochenko
Copy link
Author

babochenko commented Dec 3, 2019

@kirillgroshkov indeed, if I have such code:

@EqualsAndHashCode
public class CoolClass {
    private final String someString;
    private final int someInt;
    private final Map<String, String> someFields;

    CoolClass(String someString, int someInt, Map<String, String> someFields) {
        this.someString = someString;
        this.someInt = someInt;
        this.someFields = someFields;
    }

    @JsonCreator(mode = DELEGATING)
    public static CoolClass fromString(String someString) {
        return new CoolClass(someString, 0, emptyMap());
    }

    @JsonCreator(mode = PROPERTIES)
    public static CoolClass create(
            @JsonProperty("myString") String someString,
            @JsonProperty("myInt") int someInt,
            @JsonProperty("myFields") Map<String, String> someFields) {
        return new CoolClass(someString, someInt, someFields);
    }
}

these tests pass:

class CoolClassTest {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static Arguments[] args() {
        return new Arguments[]{
                Arguments.of(
                        "\"str\"",
                        new CoolClass("str", 0, Collections.emptyMap())),
                Arguments.of(
                        // language=json
                        "{\"myString\": \"something\", \"myInt\": 14, \"myFields\": {}}",
                        new CoolClass("something", 14, Collections.emptyMap())),
        };
    }

    @ParameterizedTest
    @MethodSource("args")
    void create(String jsonSource, CoolClass expectedResult) throws Exception {
        CoolClass actualResult = OBJECT_MAPPER.readValue(jsonSource, CoolClass.class);
        assertEquals(expectedResult, actualResult);
    }
}

tested on jackson 2.10.1
You have to quote the string for DELEGATING mode by youself though

@cowtowncoder
Copy link
Member

@stackmagic thank you for verifying this.

@cowtowncoder cowtowncoder removed the 2.11 label Apr 12, 2020
@Legion2
Copy link

Legion2 commented Oct 24, 2020

When the single argument of the delegation JsonCreator is of Type Map<String, ...>, the Property JsonCreator is never used.

@cowtowncoder
Copy link
Member

@Legion2 if you have a new problem, you want to create a new issue. We do not usually re-open closed issued.
Delegating creators can definitely use Maps too (but so can properties based ones).

@joffrey-bion
Copy link

joffrey-bion commented Mar 2, 2023

I'm hitting this problem (as stated in the issue title), and I just realized it's not possible at the moment to define multiple @JsonCreators in general. Shouldn't this issue stay open? I mean, it only works in a very specific case with only one property-based creator and one delegating creator that happens to take a scalar type. I guess the issue should either be renamed or be kept open.

I'm trying to add overloaded constructors for backwards compatibility with older JSONs and I'm at a loss at the moment. I tried to use multiple property-based creators with distinct property sets (for different old versions and the new version of the JSON), and it doesn't seem to work (Jackson complains that it already has another creator and doesn't want to add more). Any ideas how I can achieve that?

@cowtowncoder
Copy link
Member

@joffrey-bion On constructors/creators: correct -- only one instance of specific type of Creator is allowed; for Properties-based Creator just one is allowed. And then scalar-delegating ones are sort of legacy left-overs.

But as to how to support different structures, I think typical way is to bind to intermediate value (often JsonNode), then have delegating Creator that takes that value, extracts pieces necessary.

As to issue itself; I don't like re-opening closed issues (and even less about renaming to something different from the original reported problem), but anyone can re-file the request itself (and a reference to this issue as background), for specific usage part like "allow multiple Properties-based creators".

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

5 participants