Describe the bug
Parsing a SCIM search response as a ListResponse<GenericScimResource> fails with the error
ScimDeserializeException: Cannot convert a non-object JSON to a GenericScimResource
caused by
MismatchedInputException: Trailing token.
To Reproduce
Steps to reproduce the behavior, including a code example, if applicable. Full unit test at the end.
TypeReference<ListResponse<GenericScimResource>> ref =
new TypeReference<ListResponse<GenericScimResource>>() {};
var mapper = JsonUtils.createJsonMapper();
var users = mapper.readValue(JSON, ref);
Produces the error:
com.unboundid.scim2.common.exceptions.runtime.ScimDeserializeException: Cannot convert a non-object JSON to a GenericScimResource.
at [No location information] (through reference chain: com.unboundid.scim2.common.messages.ListResponse["Resources"]->java.util.ArrayList[0])
at com.unboundid.scim2.common.utils.GenericScimObjectDeserializer.deserialize(GenericScimObjectDeserializer.java:69)
at com.unboundid.scim2.common.utils.GenericScimObjectDeserializer.deserialize(GenericScimObjectDeserializer.java:49)
…
Caused by: tools.jackson.databind.exc.MismatchedInputException: Trailing token (`JsonToken.START_OBJECT`) found after value (bound as `tools.jackson.databind.node.ObjectNode`): not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: #UNKNOWN]
at tools.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:67)
at tools.jackson.databind.DeserializationContext.reportTrailingTokens(DeserializationContext.java:1941)
at tools.jackson.databind.ObjectReader._verifyNoTrailingTokens(ObjectReader.java:2079)
at tools.jackson.databind.ObjectReader._bind(ObjectReader.java:1865)
at tools.jackson.databind.ObjectReader.readValue(ObjectReader.java:1162)
at com.unboundid.scim2.common.utils.GenericScimObjectDeserializer.deserialize(GenericScimObjectDeserializer.java:64)
Expected behavior
Parsing should succeed, returning a ListResponse holding any number of GenericScimResource objects.
Additional context
Add any other context about the problem here. For example:
- Java version: 25.0.2
- SCIM 2 SDK version: 6.0.0
Suspected cause
The bug appears to lie in com.unboundid.scim2.common.utils.GenericScimObjectDeserializer.
It works if deserialising a standalone value, but fails when called to deserialise individual values in an array.
It fails because FAIL_ON_TRAILING_TOKENS is enabled (by default in jackson 3), but that isn't appropriate
when the circumstance is deserialising part of a larger JSON value.
Apparently, the proper approach is to use given DeserializationContext, which has a readTree(jp) method.
The challenge for GenericScimObjectDeserializer might be combining the "SCIM special rules" from JsonUtils.getObjectReader() with the given DeserializationContext.
Maybe adding .without(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) is sufficient?
Unit test
package com.example.scim;
import static org.junit.jupiter.api.Assertions.*;
import com.unboundid.scim2.common.GenericScimResource;
import com.unboundid.scim2.common.messages.ListResponse;
import com.unboundid.scim2.common.types.UserResource;
import com.unboundid.scim2.common.utils.JsonUtils;
import org.junit.jupiter.api.Test;
import tools.jackson.core.type.TypeReference;
class GenericScimObjectDeserializerBug {
static final String JSON =
"""
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 2,
"itemsPerPage": 2,
"startIndex": 1,
"Resources": [
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id" : "abc-123",
"userName": "alice@example.com"
},
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id" : "def-456",
"userName": "bob@example.com"
}
]
}
""";
@Test
void parseListOfGenericScimResource_FAILS() {
TypeReference<ListResponse<GenericScimResource>> ref =
new TypeReference<ListResponse<GenericScimResource>>() {};
var mapper = JsonUtils.createJsonMapper();
var users = mapper.readValue(JSON, ref);
assertEquals(2, users.getResources().size());
}
@Test
void parseListOfUserResource() {
TypeReference<ListResponse<UserResource>> ref =
new TypeReference<ListResponse<UserResource>>() {};
var mapper = JsonUtils.createJsonMapper();
var users = mapper.readValue(JSON, ref);
assertEquals(2, users.getResources().size());
}
}
Describe the bug
Parsing a SCIM search response as a
ListResponse<GenericScimResource>fails with the errorScimDeserializeException: Cannot convert a non-object JSON to a GenericScimResourcecaused by
MismatchedInputException: Trailing token.To Reproduce
Steps to reproduce the behavior, including a code example, if applicable. Full unit test at the end.
Produces the error:
Expected behavior
Parsing should succeed, returning a
ListResponseholding any number ofGenericScimResourceobjects.Additional context
Add any other context about the problem here. For example:
Suspected cause
The bug appears to lie in
com.unboundid.scim2.common.utils.GenericScimObjectDeserializer.It works if deserialising a standalone value, but fails when called to deserialise individual values in an array.
It fails because FAIL_ON_TRAILING_TOKENS is enabled (by default in jackson 3), but that isn't appropriate
when the circumstance is deserialising part of a larger JSON value.
Apparently, the proper approach is to use given
DeserializationContext, which has areadTree(jp)method.The challenge for
GenericScimObjectDeserializermight be combining the "SCIM special rules" fromJsonUtils.getObjectReader()with the givenDeserializationContext.Maybe adding
.without(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)is sufficient?Unit test