I ran into this issue when testing a Spring controller using MockMvc, Mockito and Jackson, so I made a simple class to test out how Jackson behaves. I'm using jackson-databind:2.3.1 and mockito-core:1.9.5.
我在使用MockMvc、Mockito和Jackson测试Spring控制器时遇到了这个问题,所以我创建了一个简单的类来测试Jackson的行为。我使用的是jackson-databind:2.3.1和mockito-core:1.9.5。
Given this class:
鉴于这类:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.Serializable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class Person implements Serializable {
private String name;
private int age;
// Public getters and setters...
public static void main(String[] args) {
String name = "Bob";
int age = 21;
ObjectMapper objectMapper = new ObjectMapper();
// attempt serialization with real object
Person person = new Person();
person.setName(name);
person.setAge(age);
try {
System.out.println(objectMapper.writeValueAsString(person));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize real object");
}
// attempt serialization with mock object
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
when(mockPerson.getAge()).thenReturn(age);
try {
System.out.println(objectMapper.writeValueAsString(mockPerson));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize mock object.");
}
}
Jackson has no problem serializing the real object, however it will throw a JsonMappingException when it tries to serialize the mocked object. Debugging through the code, it's calling serializeFields(bean, jgen, provider) repeatedly, getting stuck on the internal Mockito properties.
Jackson对序列化真实对象没有问题,但是当它试图序列化被模仿的对象时,会抛出一个JsonMappingException。通过代码调试,它反复调用serializeFields(bean、jgen、provider),陷入了内部Mockito属性。
So, my question is: Is there anyway to force Jackson to use the getter methods? I tried @JsonIgnoreProperties on the class, @JsonIgnore on the fields, and @JsonProperty on the methods (in different combinations, to no success). Or, do I have to write my own custom serializer?
所以,我的问题是:是否有办法强迫Jackson使用getter方法?我在类上尝试了@JsonIgnoreProperties,在字段上尝试了@JsonIgnore,在方法上尝试了@JsonProperty(不同的组合,没有成功)。或者,我必须编写自己的自定义序列化器吗?
Thanks!
谢谢!
2 个解决方案
#1
5
Here is a solution that will work for you particular case:
这里有一个针对你的特殊情况的解决方案:
First of all you need to create a PersonMixin since you cannot add the required annotations to the mock.
首先,您需要创建PersonMixin,因为您不能向mock添加所需的注释。
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public interface PersonMixin {
@JsonProperty
String getName();
@JsonProperty
Integer getAge();
}
Now, use the object mapper like the in following code and you will get the same result as when you serialize the real object:
现在,使用如下代码中的对象映射器,您将得到与序列化真实对象时相同的结果:
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
when(mockPerson.getAge()).thenReturn(age);
objectMapper.addMixInAnnotations(Person.class, PersonMixin.class);
try {
System.out.println(objectMapper.writeValueAsString(mockPerson));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize mock object.");
}
#2
3
Here's my ObjectMapper
that sorted it out without the need for mixins.
这是我的ObjectMapper,它可以在不需要混合的情况下进行分类。
The mapper ignores all members of that has "Mockito" in somewhere in their names.
mapper忽略了所有成员的名字中的“Mockito”。
This solution avoids having a mix-in for each serialized object, or annotating code that may not be accessible.
这个解决方案避免了对每个序列化的对象进行混合,或者避免了无法访问的注释代码。
Running the following test succeeds with the output {"name":"Jonh"}
.
运行下面的测试成功地使用输出{“name”:“Jonh”}。
package test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import org.mockito.Mockito;
public class AppTest extends Mockito {
public void testApp() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
@Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return super.hasIgnoreMarker(m) || m.getName().contains("Mockito");
}
});
final String name = "Jonh";
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
System.out.println(mapper.writeValueAsString(mockPerson));
}
public static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
#1
5
Here is a solution that will work for you particular case:
这里有一个针对你的特殊情况的解决方案:
First of all you need to create a PersonMixin since you cannot add the required annotations to the mock.
首先,您需要创建PersonMixin,因为您不能向mock添加所需的注释。
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public interface PersonMixin {
@JsonProperty
String getName();
@JsonProperty
Integer getAge();
}
Now, use the object mapper like the in following code and you will get the same result as when you serialize the real object:
现在,使用如下代码中的对象映射器,您将得到与序列化真实对象时相同的结果:
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
when(mockPerson.getAge()).thenReturn(age);
objectMapper.addMixInAnnotations(Person.class, PersonMixin.class);
try {
System.out.println(objectMapper.writeValueAsString(mockPerson));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize mock object.");
}
#2
3
Here's my ObjectMapper
that sorted it out without the need for mixins.
这是我的ObjectMapper,它可以在不需要混合的情况下进行分类。
The mapper ignores all members of that has "Mockito" in somewhere in their names.
mapper忽略了所有成员的名字中的“Mockito”。
This solution avoids having a mix-in for each serialized object, or annotating code that may not be accessible.
这个解决方案避免了对每个序列化的对象进行混合,或者避免了无法访问的注释代码。
Running the following test succeeds with the output {"name":"Jonh"}
.
运行下面的测试成功地使用输出{“name”:“Jonh”}。
package test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import org.mockito.Mockito;
public class AppTest extends Mockito {
public void testApp() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
@Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return super.hasIgnoreMarker(m) || m.getName().contains("Mockito");
}
});
final String name = "Jonh";
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
System.out.println(mapper.writeValueAsString(mockPerson));
}
public static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}