我有一个包含两个LocalDateTime成员的类:
public class Foo
{
private final LocalDateTime start;
private final LocalDateTime end;
public Foo(LocalDateTime start, LocalDateTime end)
{
this.start = start;
this.end = end;
}
}
我有一个Spring Boot控制器来处理POST请求:
@PostMapping(value = "/my-resource")
public ResponseEntity<?> bar(@RequestBody Foo foo)
{
return ResponseEntity.ok().build();
}
如果我发送POST请求,如下所示:
curl -X POST \
http://localhost:8080/my-resource \
-H 'Content-Type: application/json' \
-d '{
"start":[2016, 1, 1, 10, 24],
"end":[2016, 1, 1, 10, 24]
}
'
返回以下错误:
"message": "Type definition error: [simple type, class Foo]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `Foo` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (PushbackInputStream); line: 2, column: 2]"
经过一些挖掘,我向Foo添加了一个默认的ctor,从而解决了该错误。我还必须从成员中删除final
。
我不明白为什么该解决方案有效。有人可以解释吗?
答案 0 :(得分:1)
用@JsonCreator
注释构造函数:
@JsonCreator
public Foo(LocalDateTime start, LocalDateTime end) {
this.start = start;
this.end = end;
}
一旦日期使用一种非常特殊的格式,您将需要编写一个自定义的反序列化器来处理它们。
如果您坚持使用ISO 8601,则可以依靠JavaTimeModule
:它将为您提供serializers { {3}}。
查看详细信息deserializers。
答案 1 :(得分:0)
Jackson使用反射来实例化Foo
类,并且该机制要求存在默认构造函数。
在实例化Foo
类之后,再次使用反射,Jackson将设置该实例的字段值-当前所有字段值均为空。由于您声明了这些字段final
,因此无法对其进行修改。
这里是一个例子:
import java.lang.reflect.Field;
public class Reflection {
public static void main(String[] args) {
try {
Class<?> clazz = Child.class;
Object child = clazz.newInstance();
Field f1 = child.getClass().getDeclaredField("age");
// Because 'age' field is private
// we make it accesible
f1.setAccessible(true);
// Set the desired value
f1.set(child, 10);
System.out.println("Child: " + child);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Child {
private int age;
@Override
public String toString() {
return "Child [age=" + age + "]";
}
}
Jackson的序列化/反序列化机制非常灵活。您可以设置和配置很多注释。
这里是另一个示例:
public class Jackson {
public static void main(String[] args) {
try {
ObjectMapper mapper = new ObjectMapper();
String jsonAsString = "{\"age\":10}";
Child child = mapper.readValue(jsonAsString, Child.class);
System.out.println(child);
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的示例不起作用,因为age
字段没有访问器方法。为了克服这个问题,我们可以添加:
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
上面的示例现在可以正常工作。
@JB Nizet在评论中指出,here是关于杰克逊的非常好的文档。