最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Jackson Object Mapper exception with `$schema` meta data in JSON - Stack Overflow

programmeradmin2浏览0评论

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.

Share Improve this question asked Feb 7 at 16:31 grapplergrappler 5671 gold badge5 silver badges19 bronze badges 3
  • It seems like your enum is only defining TC_01, which is fine, but make sure your enum has all the possible test case values. It looks like the EnumMap requires the keys to be of enum type TestCaseEnum, but the JSON keys are strings. Try deserializing to a Map<String, MailingRequest>, You can handle the string keys, and then manually convert them to the appropriate enum values for the EnumMap. – mahbad Commented Feb 7 at 16:45
  • Do a test and remove only the $ 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
  • 1 Thank you @mahbad for the comment. The code works as is when $schema is removed from the JSON. I previously had Map<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
Add a comment  | 

2 Answers 2

Reset to default 1

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?

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

发布评论

评论列表(0)

  1. 暂无评论