You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Jan 22, 2019. It is now read-only.
When a mixin json ignore annotation (@JsonIgnore) is used, the expectation for the field is for it to be ignored. In the simple case this is what happens.
However, if @JsonProperty exists on the original model field the ignore annotation on the mixin is itself ignored and an exception can be thrown, if the model field is itself an object, or in some cases if it contains other nested objects.
I have written the following test to show the issue as I see it.
The hoped for fix would be to always have the mixin annotations take precedence over the model annotations. Otherwise, I don't think I understand the purpose of the mixin correctly.
packageinfo.afilias.intdir.core.util;
importcom.fasterxml.jackson.annotation.JsonIgnore;
importcom.fasterxml.jackson.annotation.JsonProperty;
importcom.fasterxml.jackson.core.JsonGenerationException;
importcom.fasterxml.jackson.dataformat.csv.CsvMapper;
importcom.fasterxml.jackson.dataformat.csv.CsvSchema;
importorg.junit.Rule;
importorg.junit.Test;
importorg.junit.rules.ExpectedException;
importstaticorg.hamcrest.CoreMatchers.containsString;
importstaticorg.hamcrest.MatcherAssert.assertThat;
/** * */publicclassJacksonCsvTest {
@RulepublicExpectedExceptionexpectedException = ExpectedException.none();
/** * This is the mixin happy path. The object property is ignored. */@TestpublicvoidtestMixinOverride_ExpectNoException() throwsException {
CsvNestedNestedModel2nestedNestedModel = newCsvNestedNestedModel2();
nestedNestedModel.prop1A = "prop1A";
CsvNestedModel2nestedModel = newCsvNestedModel2();
nestedModel.nestedNestedModel = nestedNestedModel;
CsvModel2csvModel = newCsvModel2();
csvModel.foo = "f1";
csvModel.nestedModel = nestedModel;
CsvMappercsvMapper = newCsvMapper();
CsvSchemaschema = csvMapper.schemaFor(CsvModel.class);
csvMapper.addMixIn(CsvModel2.class, CsvModelMixin2.class);
Stringcsv = csvMapper.writer(schema).writeValueAsString(csvModel);
assertThat(csv, containsString("f1"));
}
/** * In this case, the mixin doesn't do its job and override the annotation. * IMO this is a bug and this test should pass. */@TestpublicvoidtestMixinOverride_MixinFailsToOverride() throwsException {
CsvNestedNestedModel3nestedNestedModel = newCsvNestedNestedModel3();
nestedNestedModel.prop1A = "prop1A";
CsvNestedModel3nestedModel = newCsvNestedModel3();
nestedModel.nestedNestedModel = nestedNestedModel;
CsvModel3csvModel = newCsvModel3();
csvModel.foo = "f1";
csvModel.nestedModel = nestedModel;
CsvMappercsvMapper = newCsvMapper();
CsvSchemaschema = csvMapper.schemaFor(CsvModel.class);
csvMapper.addMixIn(CsvModel3.class, CsvModelMixin3.class);
Stringcsv = csvMapper.writer(schema).writeValueAsString(csvModel);
assertThat(csv, containsString("f1"));
}
/** * This test passes because the exception is expected when the property has an object value but that property * has not been ignored. * <p/> * It would be useful to be able to set an option for a default behaviour that would cancel the throwing of * the exception. For instance, just print "Object" or "n/a" when the value encountered is an object. * <p/> * This would make the resulting system less brittle and prone to errors when the model changes, but the * csv schema or mixing is not also updated. */@TestpublicvoidtestMixinOverride_ExpectJsonGenerationException() throwsException {
CsvNestedNestedModelnestedNestedModel = newCsvNestedNestedModel();
nestedNestedModel.prop1A = "prop1A";
CsvNestedModelnestedModel = newCsvNestedModel();
nestedModel.nestedNestedModel = nestedNestedModel;
CsvModelcsvModel = newCsvModel();
csvModel.foo = "f1";
csvModel.nestedModel = nestedModel;
CsvMappercsvMapper = newCsvMapper();
CsvSchemaschema = csvMapper.schemaFor(CsvModel.class);
csvMapper.addMixIn(CsvModel.class, CsvModelMixin.class);
expectedException.expect(JsonGenerationException.class);
expectedException.expectMessage("CSV generator does not support Object values for properties");
csvMapper.writer(schema).writeValueAsString(csvModel);
}
}
classCsvModel {
Stringfoo;
CsvNestedModelnestedModel;
publicStringgetFoo() {
returnfoo;
}
publicvoidsetFoo(Stringfoo) {
this.foo = foo;
}
publicCsvNestedModelgetNestedModel() {
returnnestedModel;
}
publicvoidsetNestedModel(CsvNestedModelnestedModel) {
this.nestedModel = nestedModel;
}
}
classCsvNestedModel {
Stringprop1;
CsvNestedNestedModelnestedNestedModel;
publicStringgetProp1() {
returnprop1;
}
publicvoidsetProp1(Stringprop1) {
this.prop1 = prop1;
}
publicCsvNestedNestedModelgetNestedNestedModel() {
returnnestedNestedModel;
}
publicvoidsetNestedNestedModel(CsvNestedNestedModelnestedNestedModel) {
this.nestedNestedModel = nestedNestedModel;
}
}
classCsvNestedNestedModel {
Stringprop1A;
publicStringgetProp1A() {
returnprop1A;
}
publicvoidsetProp1A(Stringprop1A) {
this.prop1A = prop1A;
}
}
classCsvModelMixin {
}
////////////////////////////////////////////////////////////////classCsvModel2 {
Stringfoo;
CsvNestedModel2nestedModel;
publicStringgetFoo() {
returnfoo;
}
publicvoidsetFoo(Stringfoo) {
this.foo = foo;
}
publicCsvNestedModel2getNestedModel() {
returnnestedModel;
}
publicvoidsetNestedModel(CsvNestedModel2nestedModel) {
this.nestedModel = nestedModel;
}
}
classCsvNestedModel2 {
Stringprop1;
CsvNestedNestedModel2nestedNestedModel;
publicStringgetProp1() {
returnprop1;
}
publicvoidsetProp1(Stringprop1) {
this.prop1 = prop1;
}
publicCsvNestedNestedModel2getNestedNestedModel() {
returnnestedNestedModel;
}
publicvoidsetNestedNestedModel(CsvNestedNestedModel2nestedNestedModel) {
this.nestedNestedModel = nestedNestedModel;
}
}
classCsvNestedNestedModel2 {
Stringprop1A;
publicStringgetProp1A() {
returnprop1A;
}
publicvoidsetProp1A(Stringprop1A) {
this.prop1A = prop1A;
}
}
classCsvModelMixin2 {
@JsonIgnoreCsvNestedModel2nestedModel;
}
////////////////////////////////////////////////////////////////classCsvModel3 {
Stringfoo;
// this annotation causes the mixin ignore annotation to be ignored@JsonProperty("alternateName")
CsvNestedModel3nestedModel;
publicStringgetFoo() {
returnfoo;
}
publicvoidsetFoo(Stringfoo) {
this.foo = foo;
}
publicCsvNestedModel3getNestedModel() {
returnnestedModel;
}
publicvoidsetNestedModel(CsvNestedModel3nestedModel) {
this.nestedModel = nestedModel;
}
}
classCsvNestedModel3 {
Stringprop1;
CsvNestedNestedModel3nestedNestedModel;
publicStringgetProp1() {
returnprop1;
}
publicvoidsetProp1(Stringprop1) {
this.prop1 = prop1;
}
publicCsvNestedNestedModel3getNestedNestedModel() {
returnnestedNestedModel;
}
publicvoidsetNestedNestedModel(CsvNestedNestedModel3nestedNestedModel) {
this.nestedNestedModel = nestedNestedModel;
}
}
classCsvNestedNestedModel3 {
Stringprop1A;
publicStringgetProp1A() {
returnprop1A;
}
publicvoidsetProp1A(Stringprop1A) {
this.prop1A = prop1A;
}
}
classCsvModelMixin3 {
@JsonIgnoreCsvNestedModel3nestedModel;
}
The text was updated successfully, but these errors were encountered:
First of all, thank you for reporting this problem.
I will have to read the model with thought to make sure I understand what is happening here, but I have a guess as to how things may go wrong.
First: mix-ins are primarily meant to augment annotation set (that is, to add configuration in cases where no annotations exist). But overriding of specific annotations should also work, such that annotations from mix-in should work as if they replaced specific annotation in target class.
Second: when merging mix-ins, all annotations are handled separately, without knowing expected semantics or dependencies between types. For example, there is no special handling to try to merge @JsonIgnore and @JsonProperty information since mix-in merging code has no knowledge that they somehow relate to each other.
When applying annotation information in determining how things should work, there is the question of what should happen if both @JsonIgnore and @JsonProperty exist for a method or field: which one should have precedence? It may be possible to determine that one is more specific, like when being applied to a sub-class; although there may also be cases where there is no clear precedence (annotations come from interfaces, for example).
If I remember things correctly, @JsonIgnore in general should have precedence. So it would seem that your usage should work, regardless of inheritance hierarchy.
I also assume this is not CSV-specific, although you have encountered it with CSV module.
This is mostly important for me to know what is the best place to reproduce the problem: if it applies to other datatypes, it should be in core databind; but if only CSV, could be due to something within this module.
Hi Tatu, thanks very much for this response. I gather that you believe this is a defect and will be looking at it further in the future. Thanks for these very useful libraries.
When a mixin json ignore annotation (
@JsonIgnore
) is used, the expectation for the field is for it to be ignored. In the simple case this is what happens.However, if
@JsonProperty
exists on the original model field the ignore annotation on the mixin is itself ignored and an exception can be thrown, if the model field is itself an object, or in some cases if it contains other nested objects.I have written the following test to show the issue as I see it.
The hoped for fix would be to always have the mixin annotations take precedence over the model annotations. Otherwise, I don't think I understand the purpose of the mixin correctly.
The text was updated successfully, but these errors were encountered: