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

Test case to document behaviour of Record x Single-value Constructor x JsonValue annotation, for #3180 #3738

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fasterxml.jackson.databind.records;

import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
Expand Down Expand Up @@ -29,6 +30,18 @@ public static RecordWithImplicitFactoryMethods valueOf(String id) {
record RecordWithSingleValueConstructor(int id) {
}

record RecordWithSingleValueConstructorWithJsonValue(@JsonValue int id) {
}

record RecordWithSingleValueConstructorWithJsonValueAccessor(int id) {

@JsonValue
@Override
public int id() {
return id;
}
}

record RecordWithNonCanonicalConstructor(int id, String name, String email) {

public RecordWithNonCanonicalConstructor(int id, String email) {
Expand Down Expand Up @@ -119,8 +132,18 @@ public void testDeserializeUsingImplicitFactoryMethod_WithAutoDetectCreatorsDisa
* GOTCHA: For JavaBean, only having single-value constructor results in implicit delegating creator. But for
* Records, the CANONICAL single-value constructor results in properties-based creator.
* <p/>
* Only when there's NON-CANONICAL single-value constructor will there be implicit delegating creator - see
* {@link #testDeserializeUsingImplicitDelegatingConstructor()}.
* It will result in implicit delegating constructor only when:
* <ul>
* <li>
* There's NON-CANONICAL single-value constructor - see
* {@link #testDeserializeUsingImplicitDelegatingConstructor()}, or
* </li>
* <li>
* {@code @JsonValue} annotation is used - see
* {@link #testDeserializeUsingImplicitSingleValueConstructor_WithJsonValue()},
* {@link #testDeserializeUsingImplicitSingleValueConstructor_WithJsonValueAccessor()}
* </li>
* </ul>.
* <p/>
* yihtserns: maybe we can change this to adopt JavaBean's behaviour, but I prefer to not break existing behaviour
* until and unless there's a discussion on this.
Expand Down Expand Up @@ -190,6 +213,64 @@ public void testDeserializeSingleValueConstructor_WithPropertiesBasedConstructor
assertEquals(new RecordWithSingleValueConstructor(123), value);
}

/*
/**********************************************************************
/* Test methods, implicit single-value constructor + @JsonValue
/**********************************************************************
*/

/**
* [databind#3180]
* This test-case is just for documentation purpose:
* Unlike {@link #testDeserializeUsingImplicitSingleValueConstructor()}, annotating {@code @JsonValue}
* to a Record's header results in a delegating constructor.
*/
public void testDeserializeUsingImplicitSingleValueConstructor_WithJsonValue() throws Exception {
// Can use delegating creator
RecordWithSingleValueConstructorWithJsonValue value = MAPPER.readValue(
"123",
RecordWithSingleValueConstructorWithJsonValue.class);
assertEquals(new RecordWithSingleValueConstructorWithJsonValue(123), value);

try {
// Can no longer use properties-based creator
MAPPER.readValue("{\"id\":123}", RecordWithSingleValueConstructorWithJsonValue.class);

fail("should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Cannot construct instance");
verifyException(e, "RecordWithSingleValueConstructorWithJsonValue");
verifyException(e, "although at least one Creator exists");
verifyException(e, "cannot deserialize from Object value");
}
}

/**
* [databind#3180]
* This test-case is just for documentation purpose:
* Unlike {@link #testDeserializeUsingImplicitSingleValueConstructor()}, annotating {@code @JsonValue}
* to the accessor results in a delegating creator.
*/
public void testDeserializeUsingImplicitSingleValueConstructor_WithJsonValueAccessor() throws Exception {
// Can use delegating creator
RecordWithSingleValueConstructorWithJsonValueAccessor value = MAPPER.readValue(
"123",
RecordWithSingleValueConstructorWithJsonValueAccessor.class);
assertEquals(new RecordWithSingleValueConstructorWithJsonValueAccessor(123), value);

try {
// Can no longer use properties-based creator
MAPPER.readValue("{\"id\":123}", RecordWithSingleValueConstructorWithJsonValueAccessor.class);

fail("should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Cannot construct instance");
verifyException(e, "RecordWithSingleValueConstructorWithJsonValueAccessor");
verifyException(e, "although at least one Creator exists");
verifyException(e, "cannot deserialize from Object value");
}
}

/*
/**********************************************************************
/* Test methods, implicit properties-based + delegating constructor
Expand Down