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

Support for serializing Java Records #94

Closed
jam01 opened this issue Feb 22, 2022 · 11 comments
Closed

Support for serializing Java Records #94

jam01 opened this issue Feb 22, 2022 · 11 comments
Labels
Milestone

Comments

@jam01
Copy link

jam01 commented Feb 22, 2022

Hey all, I did not find any explicit claim that Java Records were supported so I decided to give it a shot. It seems to me that they are not, given the following snippet:

    @Test
    public void testRecord() throws IOException {
        var json = """
                {
                    "string":"foo",
                    "object": {
                        "foo": "bar"
                    }
                }
                """;

        var rec = JSON.std.beanFrom(Cow.class, json);
        System.out.println(rec);
    }

    public record Cow(String message, Map<String, String> object) {}

results in:

com.fasterxml.jackson.jr.ob.JSONObjectException: Failed to create an instance of com.example.Cow due to (java.lang.IllegalStateException): Class com.example.Cow does not have default constructor to use
 at [Source: (String)"{
    "message":"foo",
    "mappy": {
        "foo": "bar"
    }
}
"; line: 1, column: 1]

	at com.fasterxml.jackson.jr.ob.JSONObjectException.from(JSONObjectException.java)
	at com.fasterxml.jackson.jr.ob.impl.BeanReader.read(BeanReader.java)
	at com.fasterxml.jackson.jr.ob.impl.JSONReader.readBean(JSONReader.java)
	at com.fasterxml.jackson.jr.ob.JSON.beanFrom(JSON.java)
	at com.example.AppTest.testRecord(AppTest.java:30)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalStateException: Class com.example.Cow does not have default constructor to use
	at com.fasterxml.jackson.jr.ob.impl.BeanReader.create(BeanReader.java)
	... 73 more

Perhaps I'm missing something that'd make this work, but if not I thought it'd be a good wish list addition 🙂

@cowtowncoder
Copy link
Member

Correct: jackson-jr has no support for records at this point.

I agree it'd be absolutely great to support them: possibly as a separate extension as that would allow code to use JDK 17 level (without core jackson-jr having to).

@cowtowncoder cowtowncoder changed the title support for records Support for serializing Java Records Feb 23, 2024
@cowtowncoder
Copy link
Member

note: changed title to reflect that first we'll be getting support for serializing (writing Records as JSON): deserialization would be later development.

@cowtowncoder cowtowncoder added this to the 2.17.0 milestone Mar 11, 2024
@cowtowncoder
Copy link
Member

Implemented as per #130; needs to be enabled with

JSON.Feature.USE_FIELD_MATCHING_GETTERS

to be included in 2.17.0 release

@bmaggi
Copy link

bmaggi commented May 28, 2024

Hi,

deserialization would be later development.

Is there any ongoing work on this part ?
I don't see any issue on the subject (maybe I missed something)
https://github.com/FasterXML/jackson-jr/issues?q=is%3Aissue+is%3Aopen+record

Thanks

@cowtowncoder
Copy link
Member

@bmaggi I am not working on this: contributions would probably be needed for progress. Note was not meant to indicate any specific plans but rather that for now, only serialization support has been added.
And if anyone else was working on it there'd probably be a PR.

@bmaggi
Copy link

bmaggi commented May 29, 2024

@cowtowncoder Thanks for the clarification.

@cowtowncoder
Copy link
Member

cowtowncoder commented Jun 14, 2024

Note: support for Java Record deserialization with jackson-jr was added via #148 -- to be include in 2.18.0.

/cc @bmaggi

EDIT: had to revert initial deserialization support -- does not really work, except if all fields were included in exactly right order: Reordered or missing fields fail spectacularly, and implementation did not account for either.

@bmaggi
Copy link

bmaggi commented Jun 20, 2024

I tested it on my project and it seems to works perfectly. Thanks

@cowtowncoder
Copy link
Member

@bmaggi Alas, even if deserialization worked in "everything exactly right" case, it had fundamental problem of blindly assuming fields are included in exact order. So I had to revert the PR and as of now, support for Record deser does not exist.

@bmaggi
Copy link

bmaggi commented Jun 20, 2024

Indeed, my use case was using fields in exact order.
Thanks for keeping me up to date.

@cowtowncoder
Copy link
Member

@bmaggi Yeah it is not an unusual case as Jackson-jr (and Jackson) would be default serialize exactly that way. That's part of why I missed the obvious-in-hindsight problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants