我正在使用Spring-MVC
,并且具有如下所示的DTO,以便从客户端(一个JSON
实体)接收foo
数据,并使用JPA
将其保存到数据库中:
public class FooDTO {
public Integer id;
public String label;
public Double amount;
public List<Integer> states;
...
但是当客户想要编辑foo
实体时,我必须像下面这样构造它
public class FooDTO {
public Integer id;
public String label;
public Double amount;
public List<SimpleDto> states;
...
使用SimpleDto
public class SimpleDto {
public Integer value;
public String label;
}
区别只是states
类型,有时是List<SimpleDto>
,有时是List<Integer>
,而且我不想创建另一个dto。
那么如何在dto(json)中实现动态字段类型?
P.S JSON数据由com.fasterxml.jackson.core
答案 0 :(得分:5)
使用自定义解串器是解决问题的一种方法
http
发送请求并回打印的控制器
public class DynamicDeserializer extends JsonDeserializer {
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String requestString = jp.readValueAsTree().toString();
JSONArray jo = new JSONArray(requestString);
List<SimpleDto> simpleDtoList = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
if(jo!=null && jo.length()>0) {
for (int i = 0; i < jo.length(); i++) {
Object string = jo.get(0);
if(string!=null && string instanceof JSONObject){
JSONObject value = jo.getJSONObject(i);
SimpleDto simpleDto = new SimpleDto();
simpleDto.setValue(value.getInt("value"));
simpleDtoList.add(simpleDto);
}else{
integers.add(jo.getInt(0));
}
}
}
return integers.isEmpty() ? simpleDtoList:integers;
}
}
存在通用映射的pojo类
@PostMapping("/test")
public Optional<TestObject> testDynamicMapper(
@RequestBody final TestObject testObject) {
List<Object> states = testObject.getStates();
for (Object object:states) {
if(object instanceof SimpleDto){
SimpleDto dto = (SimpleDto)object;
System.out.println(dto.getValue());
}
if(object instanceof Integer){
Integer dto = (Integer)object;
System.out.println(dto);
}
}
return Optional.of(testObject);
}
public class TestObject implements Serializable {
@JsonDeserialize(using = DynamicDeserializer.class)
private List<Object> states;
public List<Object> getStates() {
return states;
}
public void setStates(List<Object> states) {
this.states = states;
}
}
{
"states": [
{
"label": "1",
"value": 0
}
]
}
答案 1 :(得分:0)
为您的DTO类型使用弹簧类型转换器。这样,客户端可以发布stateId,并且Converter将为给定ID解析正确的DTO类型。
答案 2 :(得分:0)
我建议您使用其他类:FooInfoDTO,FooDetailsDTO。当您有主从表单时,通常使用它。在主表中,显示有关对象的简短信息(一个DTO),然后导航至详细信息,以获取完整的对象数据(另一个DTO)
答案 3 :(得分:0)
我建议不要添加另一个促进重复的DTO。 但是,您仍然需要添加另一个专用于您各自服务的DTO。您只需使用层次结构定义DTO。
public class FooDTO {
public Integer id;
public String label;
public Double amount;
}
通过扩展通用详细信息DTO(即FooDTO)来定义您的响应DTO以提供详细信息,
public class FooDetailsOutDTO extends FooDTO {
public List<Integer> states;
}
要进行编辑,请按以下方式定义DTO,
public class FooUpdateDetailsInDTO extends FooDTO {
public List<SimpleDto> states;
}
答案 4 :(得分:0)
您可以使用JsonCreator批注和项目Node
的两个构造函数。如果数组POJO
中有一个原语,将使用构造函数。对于完全设置的对象1-arg
,将使用构造函数。参见以下示例:
2-arg
上面的代码显示:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
String json = "{\"id\":1,\"label\":\"LABEL\",\"amount\":1.23,\"states\":[1,{\"value\":2},{\"value\":3,\"label\":\"LAB\"}]}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);
System.out.println(foo);
}
}
class Foo {
private Integer id;
private String label;
private Double amount;
private List<State> states;
// getters, setters, toString
}
class State {
private Integer value;
private String label;
@JsonCreator(mode = Mode.DELEGATING)
public State(@JsonProperty("value") Integer value) {
this(value, null);
}
@JsonCreator
public State(@JsonProperty("value") Integer value, @JsonProperty("label") String label) {
this.value = value;
this.label = label;
}
// getters, setters, toString
}
使用的版本:2.9.8
答案 5 :(得分:0)
写一个dto
public class FooDTO {
public Integer id;
public String label;
public Double amount;
public List<Object> states;
}
在Service类中对DTO进行类型转换并处理异常
答案 6 :(得分:0)
另一种重新建模方法:
public class FooDTO {
public Integer id;
public String label;
public Double amount;
//not null!
public List<Integer> states;
//nullable!!
... List<String> stateLabels;
// you should ensure "stable/consistent index/ordering" (relative to states)
...
...并相应地将其用于“获取”(单独访问标签)和“发布”(忽略标签;)
---------------------------------------------
甚至更好:
Map<Integer, String> states; // !?
答案 7 :(得分:0)
您只需添加一个在SimpleDto
添加使用简单的Java Stream在List<Integer>
中返回FooDTO
的getter,该Java流使用DTO getter映射到Integer
states.stream().map(SimpleDto::getValue).collect(Collectors.toList());
答案 8 :(得分:0)
您可以尝试重新设计整个体系结构。使用相关集合拆分主要实体。
提供独立服务,以根据您的实体添加/删除/设置状态。通过这种方式,您可以轻松地向您的客户端提供REST服务,这些服务将很容易使用。
这是通过REST接口实现的一组可能的方法:
@Path(../foo)
@Produces
public interface FooService {
//CRUD methods on Foo itself which work with attributes of Foo only
...
@GET
@Path("/{fooId}")
FooDTO findById(@PathParam("fooId") int fooId);
//status-related methods:
@GET
@Path("/{fooId}/status")
List<SimpleDto> statuses(@PathParam("fooId") int fooId);
@Post
@Path("/{fooId}/status")
void addStatus(@PathParam("fooId") int fooId, int statusId);
@DELETE
@Path("{fooId}/status")
void deleteStatus(@PathParam("fooId") int fooId, int statusId);
@PUT
@Path("/status")
void setStatuses(@PathParam("fooId") int fooId, List<Integer> newStatuses);
}
使用此解决方案,还有一些替代选项,我希望返回:
@GET
@Path("/{fooId}/status")
List<Integer> statuses(@PathParam("fooId") int fooId);
代替DTO列表。然后提供一项服务,以获取所有状态及其名称,而无需连接到Foo:
public interface StatusService {
List<SimpleDto> statuses();
}
为简化GUI组件的实现,您可以创建Rest服务,该服务将返回合并的数据,例如在您的第二个FooDto版本中。它还将减少休息电话的数量。但是使用单独的方法直接处理一组项目会很有帮助。
答案 9 :(得分:-1)
您可以使用generics
,将List<Integer> states
更改为List<?> states