I am getting an MismatchedInputException
from Jackson ObjectMapper. Once I remove $schema
meta data from the JSON the exception disappears. How can I keep $schema
in the JSON?
The code in Java looks like the following:
ObjectMapper objectMapper = new ObjectMapper();
EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = objectMapper.readValue(
MailingRequestFactory.class.getResource("/test-data.json"),
new TypeReference<EnumMap<TestCaseEnum, MailingRequest>>() {}
);
public enum TestCaseEnum {
TC_01;
}
@Data
@Jacksonized @Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class MailingRequest {
private String mailingId;
}
The JSON file has $schema
in it.
{
"$schema": ";,
"TC_01": {
"mailingId": "00"
}
}
I have also tried objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
which did not work.
I am getting an MismatchedInputException
from Jackson ObjectMapper. Once I remove $schema
meta data from the JSON the exception disappears. How can I keep $schema
in the JSON?
The code in Java looks like the following:
ObjectMapper objectMapper = new ObjectMapper();
EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = objectMapper.readValue(
MailingRequestFactory.class.getResource("/test-data.json"),
new TypeReference<EnumMap<TestCaseEnum, MailingRequest>>() {}
);
public enum TestCaseEnum {
TC_01;
}
@Data
@Jacksonized @Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class MailingRequest {
private String mailingId;
}
The JSON file has $schema
in it.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"TC_01": {
"mailingId": "00"
}
}
I have also tried objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
which did not work.
2 Answers
Reset to default 1I am getting an MismatchedInputException from Jackson ObjectMapper. Once I remove $schema meta data from the JSON the exception disappears. How can I keep $schema in the JSON?
This happens because the $schema
property present in the json file is recognized as a property that cannot to be deserialized as an entry of your EnumMap<TestCaseEnum, MailingRequest>>
as you want. One possible solution is remove the unwanted property from the ObjectNode
obtained from the json and after convert the ObjectNode
object to the EnumMap
like below:
ObjectMapper objectMapper = new ObjectMapper();
String s = """
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"TC_01": {
"mailingId": "00"
}
}
""";
//conversion to ObjectNode through a previous conversion of json to a JsonNode
ObjectNode root = objectMapper.readTree(s).deepCopy();
root.remove("$schema");
TypeReference<EnumMap<TestCaseEnum, MailingRequest>> ref = new TypeReference<>(){};
EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = objectMapper.convertValue(root, ref);
//ok it prints {TC_01=MailingRequest(mailingId=00)} as expected
System.out.println(mailingRequestCache);
In this way the original json remains unaltered and if you have more than one sparse property to be erased the method can be applied in this scenario too.
I found three other solutions. Not necessarily better, just different. My preference is the first.
1. Using Annotations
Create a new Object where $schema
is ignored.
@JsonIgnoreProperties({ "$schema" })
public static class SchemaData {
@JsonProperty("$schema")
private String schema;
private final EnumMap<TestCaseEnum, MailingRequest> testCases = new EnumMap<>(TestCaseEnum.class);
@JsonAnyGetter
public EnumMap<TestCaseEnum, MailingRequest> getTestCases() {
return testCases;
}
@JsonAnySetter
public void addEntry(String key, MailingRequest value) {
TestCaseEnum testCase = TestCaseEnum.valueOf(key);
testCases.put(testCase, value);
}
}
Use the new SchemaData Object
mailingRequestCache = mapper.readValue(
MailingRequestFactory.class.getResource("/test-data.json"),
SchemaData.class
).getTestCases();
2. Custom TokenFilter
Create custom TokenFilter to filter $schema
public static class SchemaSkippingFilter extends TokenFilter {
@Override
public TokenFilter includeProperty(String name) {
if ("$schema".equals(name)) {
// Skip the $schema property
return null;
}
// Include other properties
return TokenFilter.INCLUDE_ALL;
}
}
Apply the custom TokenFilter before deserializing
ObjectMapper mapper = new ObjectMapper();
FilteringParserDelegate filteringParser = new FilteringParserDelegate(
mapper.createParser(MailingRequestFactory.class.getResource("/test-data/test-data.json")),
new SchemaSkippingFilter(),
TokenFilter.Inclusion.INCLUDE_ALL_AND_PATH,
true
);
EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = mapper.readValue(filteringParser, new TypeReference<EnumMap<TestCaseEnum, MailingRequest>>() {});
3. Custom Deserializer
Create a new dummy class that extends the original structure and adds @JsonDeserialize(using = TestCaseDeserializer.class)
@JsonDeserialize(using = TestCaseDeserializer.class)
public final class SchemaData extends EnumMap<TestCaseEnum, MailingRequest> {
public SchemaData(EnumMap<TestCaseEnum, ? extends MailingRequest> m) {
super(m);
}
}
Create the TestCaseDeserializer to ignore if the key is $schema
.
public class TestCaseDeserializer extends JsonDeserializer<EnumMap<TestCaseEnum, MailingRequest>> {
@Override
public EnumMap<TestCaseEnum, MailingRequest> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
JsonNode rootNode = mapper.readTree(jsonParser);
EnumMap<TestCaseEnum, MailingRequest> testCases = new EnumMap<>(TestCaseEnum.class);
Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
String key = field.getKey();
JsonNode value = field.getValue();
if ("$schema".equals(key)) {
// Ignore this key
continue;
}
MailingRequest entry = mapper.treeToValue(value, MailingRequest.class);
testCases.put(Enum.valueOf(TestCaseEnum.class, key), entry);
}
return testCases;
}
}
ObjectMapper objectMapper = new ObjectMapper();
EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = objectMapper.readValue(
MailingRequestFactory.class.getResource("/test-data.json"),
new TypeReference<SchemaData>() {}
);
Reference: https://www.baeldung.com/jackson-deserialization
TC_01
, which is fine, but make sure your enum has all the possible test case values. It looks like theEnumMap
requires the keys to be of enum typeTestCaseEnum
, but the JSON keys are strings. Try deserializing to aMap<String, MailingRequest>
, You can handle the string keys, and then manually convert them to the appropriate enum values for theEnumMap
. – mahbad Commented Feb 7 at 16:45$
from the$schema
with the configuration of unknown fields to false and see what happens. If the error doesn't happen it might mean that Jackson is trying to parse the "$" to something. – Jorge Campos Commented Feb 7 at 18:46$schema
is removed from the JSON. I previously hadMap<String, MailingRequest>
and I had the same problem. @JorgeCampos I'll try that later, but I don't think that is the problem. The problem is that the value of$schema
is a string and not the MailingRequest object. – grappler Commented Feb 8 at 17:10