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

Serialization failure for fields after updating to Jackson 2.18.0 #843

Closed
3 tasks done
arvgord opened this issue Oct 15, 2024 · 5 comments
Closed
3 tasks done

Serialization failure for fields after updating to Jackson 2.18.0 #843

arvgord opened this issue Oct 15, 2024 · 5 comments
Labels

Comments

@arvgord
Copy link

arvgord commented Oct 15, 2024

Search before asking

  • I searched in the issues and found nothing similar.
  • I searched in the issues of databind and other modules used and found nothing similar.
  • I have confirmed that the problem only occurs when using Kotlin.

Describe the bug

After upgrading Jackson from version 2.17.2 to 2.18.0, the serialization process for SecondObject and ThirdObject has completely broken. The fields are either missing or partially serialized, making the resulting JSON incomplete.

I expected that only the field order might change, but now fields that were present in the input JSON are missing from the serialized output entirely.

To Reproduce

Tested objects:

@JsonAutoDetect(
    fieldVisibility = JsonAutoDetect.Visibility.ANY,
    getterVisibility = JsonAutoDetect.Visibility.NONE
)
class FirstObject {
    @field:JsonAnySetter
    @field:JsonAnyGetter
    val data: Map<String, Any?> = LinkedHashMap()

    val transactionId: String
        get() = data["transactionId"] as String
}

@JsonAutoDetect(
    fieldVisibility = JsonAutoDetect.Visibility.ANY,
    getterVisibility = JsonAutoDetect.Visibility.NONE
)
class SecondObject(
    val transactionId: String
) {
    @field:JsonAnySetter
    @field:JsonAnyGetter
    val data: Map<String, Any?> = LinkedHashMap()
}

@JsonAutoDetect(
    fieldVisibility = JsonAutoDetect.Visibility.ANY,
    getterVisibility = JsonAutoDetect.Visibility.NONE
)
class ThirdObject(
    val transactionId: String,
    @field:JsonAnySetter
    @field:JsonAnyGetter
    val data: Map<String, Any?> = LinkedHashMap()
)

Input JSON for tests:

{
  "b": 2,
  "a": 1,
  "transactionId": "test",
  "c": [
    {
      "id": "3",
      "value": "c"
    },
    {
      "id": "1",
      "value": "a"
    },
    {
      "id": "2",
      "value": "b"
    }
  ]
}

Test Case:

Here’s a test case to reproduce the issue. The following Kotlin test serializes and deserializes three different classes (FirstObject, SecondObject, and ThirdObject), comparing the serialized JSON with the original input JSON.
JacksonSortingTest.kt branch 2.18.0.

For FirstObject, however, the test passed successfully. All fields were present in the serialized JSON, and they appeared in the expected order, as in the original input JSON.

Failing Test Results:

Expected:

{
  "b": 2,
  "a": 1,
  "transactionId": "test",
  "c": [
    {
      "id": "3",
      "value": "c"
    },
    {
      "id": "1",
      "value": "a"
    },
    {
      "id": "2",
      "value": "b"
    }
  ]
}

For SecondObject was:

{
  "transactionId": "test",
  "c": [
    {
      "id": "3",
      "value": "c"
    },
    {
      "id": "1",
      "value": "a"
    },
    {
      "id": "2",
      "value": "b"
    }
  ]
}

For ThirdObject was:

{
  "transactionId": "test"
}

Repository for Reproduction:
You can find a repository with the full reproduction of the issue at jackson-databind-sorting-issue - branch 2.18.0.

Expected behavior

After updating from Jackson 2.17.2 to 2.18.0, the serialization for SecondObject and ThirdObject should work as it did in the previous version (except for field ordering after deserialization and serialization). The expected JSON output after deserialization and serialization should contain all fields, with their values exactly as they are in the original input, regardless of field order.

Versions

Kotlin: 2.0.0
Jackson-module-kotlin: 2.18.0
Jackson-databind: 2.18.0

Additional context

This issue is a duplicate of 4752. I'm trying to reproduce it with Java, but it seems like all the described cases are specific to Kotlin

@arvgord arvgord added the bug label Oct 15, 2024
lberrymage added a commit to accrescent/parcelo that referenced this issue Oct 24, 2024
Running Parcelo in a container via our current Dockerfile revealed that
"uses-sdk" fields were not being parsed from applications' Android
manifests, effectively preventing app uploads since the targetSdk
property of uses-sdk is required by Parcelo. This bug wasn't caught
until now because it only seems to manifest itself when running via the
Dockerfile; running locally as in our recommended development
environment does not have the issue. The Jackson 2.18.0 upgrade has not
yet been included in a production release, so it's uncertain whether the
issue would have surfaced in our production environment.

We tracked the issue down to a regression in Jackson 2.18.0. The exact
cause is unknown. However, a number of seemingly related issues were
reported for Jackson 2.18.0 [1], so we plan to closely monitor those
issues and test upcoming Jackson releases carefully to prevent breakage.

[1]: See below:

- FasterXML/jackson-module-kotlin#841
- FasterXML/jackson-module-kotlin#842
- FasterXML/jackson-module-kotlin#843
- FasterXML/jackson-module-kotlin#832
- FasterXML/jackson-databind#4508
- FasterXML/jackson-databind#4752
@k163377
Copy link
Contributor

k163377 commented Oct 26, 2024

I was able to create a Java only reproduction based on the decompiled results.
Please re-open the databind issue or submit a new issue.

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.LinkedHashMap;
import java.util.Map;

public final class GitHub843Java {
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final String jsonInput =
            "{\n" +
            "    \"b\": 2,\n" +
            "    \"a\": 1,\n" +
            "    \"transactionId\": \"test\",\n" +
            "    \"c\": [\n" +
            "        {\n" +
            "            \"id\": \"3\",\n" +
            "            \"value\": \"c\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\": \"1\",\n" +
            "            \"value\": \"a\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\": \"2\",\n" +
            "            \"value\": \"b\"\n" +
            "        }\n" +
            "    ]\n" +
            "}";

    private <T> void testSerializationDeserialization(Class<T> clazz) throws JsonProcessingException {
        Object deserializedObject = this.objectMapper.readValue(this.jsonInput, clazz);
        String serializedJson = this.objectMapper.writeValueAsString(deserializedObject);
        JsonNode expectedJson = this.objectMapper.readTree(this.jsonInput);
        JsonNode actualJson = this.objectMapper.readTree(serializedJson);
        Assertions.assertEquals(expectedJson, actualJson);
    }

    @Test
    public void test_serialization_and_deserialization_for_FirstObject() throws JsonProcessingException {
        this.testSerializationDeserialization(FirstObject.class);
    }

    @Test
    public void test_serialization_and_deserialization_for_SecondObject() throws JsonProcessingException {
        this.testSerializationDeserialization(SecondObject.class);
    }

    @Test
    public void test_serialization_and_deserialization_for_ThirdObject() throws JsonProcessingException {
        this.testSerializationDeserialization(ThirdObject.class);
    }

    @JsonAutoDetect(
            getterVisibility = Visibility.NONE,
            fieldVisibility = Visibility.ANY
    )
    public static final class FirstObject {
        @JsonAnySetter
        @JsonAnyGetter
        private final Map<String, Object> data = new LinkedHashMap<>();

        public Map<String, Object>  getData() {
            return this.data;
        }

        public String getTransactionId() {
            Object var1 = this.data.get("transactionId");
            return (String) var1;
        }
    }

    @JsonAutoDetect(
            getterVisibility = Visibility.NONE,
            fieldVisibility = Visibility.ANY
    )
    public static final class SecondObject {
        private final String transactionId;
        @JsonAnySetter
        @JsonAnyGetter
        private final Map<String, Object>  data;

        @JsonCreator
        public SecondObject(@JsonProperty("transactionId") String transactionId) {
            this.transactionId = transactionId;
            this.data = new LinkedHashMap<>();
        }

        public String getTransactionId() {
            return this.transactionId;
        }

        public Map<String, Object> getData() {
            return this.data;
        }
    }

    @JsonAutoDetect(
            getterVisibility = Visibility.NONE,
            fieldVisibility = Visibility.ANY
    )
    public static final class ThirdObject {
        private final String transactionId;
        @JsonAnySetter
        @JsonAnyGetter
        private final Map<String, Object> data;

        @JsonCreator
        public ThirdObject(
                @JsonProperty("transactionId") String transactionId,
                @JsonProperty("data") Map<String, Object> data
        ) {
            this.transactionId = transactionId;
            this.data = data;
        }

        public String getTransactionId() {
            return this.transactionId;
        }

        public Map<String, Object> getData() {
            return this.data;
        }
    }
}

@k163377 k163377 closed this as completed Oct 26, 2024
arvgord added a commit to arvgord/jackson-databind-sorting-issue-java that referenced this issue Oct 26, 2024
arvgord added a commit to arvgord/jackson-databind-sorting-issue-java that referenced this issue Oct 26, 2024
arvgord added a commit to arvgord/jackson-databind-sorting-issue-java that referenced this issue Oct 26, 2024
arvgord added a commit to arvgord/jackson-databind-sorting-issue-java that referenced this issue Oct 26, 2024
@arvgord
Copy link
Author

arvgord commented Oct 26, 2024

@k163377, thank you! I was able to fully reproduce the incorrect behavior in Java using your code (test file).

@arvgord
Copy link
Author

arvgord commented Oct 26, 2024

I reopened the issue in jackson-databind.

lberrymage added a commit to accrescent/parcelo that referenced this issue Oct 27, 2024
Running Parcelo in a container via our current Dockerfile revealed that
"uses-sdk" fields were not being parsed from applications' Android
manifests, effectively preventing app uploads since the targetSdk
property of uses-sdk is required by Parcelo. This bug wasn't caught
until now because it only seems to manifest itself when running via the
Dockerfile; running locally as in our recommended development
environment does not have the issue. The Jackson 2.18.0 upgrade has not
yet been included in a production release, so it's uncertain whether the
issue would have surfaced in our production environment.

We tracked the issue down to a regression in Jackson 2.18.0. The exact
cause is unknown. However, a number of seemingly related issues were
reported for Jackson 2.18.0 [1], so we plan to closely monitor those
issues and test upcoming Jackson releases carefully to prevent breakage.

[1]: See below:

- FasterXML/jackson-module-kotlin#841
- FasterXML/jackson-module-kotlin#842
- FasterXML/jackson-module-kotlin#843
- FasterXML/jackson-module-kotlin#832
- FasterXML/jackson-databind#4508
- FasterXML/jackson-databind#4752
@thejeff77
Copy link

So.. what version is the fix in? Can anyone post here?

@cowtowncoder
Copy link
Member

@thejeff77 as per FasterXML/jackson-databind#4752 -- which was linked above and you may have noticed -- suggests relevant fix in 2.18.1.
But I would try 2.18.2 since that is the latest patch and has other possibly relevant fixes.

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

4 participants