Java DTO中的动态字段类型
我使用的是Java DTO中的动态字段类型,java,spring,spring-mvc,jackson,Java,Spring,Spring Mvc,Jackson,我使用的是springmvc,我有一个如下结构的DTO,从客户机(一个foo实体)接收JSON数据,并将其保存到数据库中JPA: public class FooDTO { public Integer id; public String label; public Double amount; public List<Integer> states; ... 使用SimpleDto public class SimpleDto {
springmvc
,我有一个如下结构的DTO,从客户机(一个foo
实体)接收JSON
数据,并将其保存到数据库中JPA
:
public class FooDTO {
public Integer id;
public String label;
public Double amount;
public List<Integer> states;
...
使用SimpleDto
public class SimpleDto {
public Integer value;
public String label;
}
区别在于状态
类型有时是列表
,有时是列表
,我不想创建另一个dto
那么,如何在dto(json)中实现动态字段类型呢
p.S JSON数据由
com.fasterxml.jackson.core
为DTO类型使用Spring类型转换器处理。这样,客户端可以发布stateId,转换器将解析给定ID的正确DTO类型
这里有一个例子:您可以使用
泛型
,将列表状态
更改为列表状态
我建议您使用不同的类:FooInfoDTO、foodailsdto。它通常在您拥有主详细信息表单时使用。在master(表)中,您显示有关对象的简短信息(一个DTO),然后导航到详细信息,您将获取完整的对象数据(另一个DTO)我建议不要添加另一个促进重复的DTO。
但是,您仍然需要添加另一个DTO,该DTO将专用于您各自的服务。您只需使用层次结构定义DTO
public class FooDTO {
public Integer id;
public String label;
public Double amount;
}
定义您的响应DTO以提供详细信息,方法是将通用详细信息DTO扩展为FooDTO,如下所示:
public class FooDetailsOutDTO extends FooDTO {
public List<Integer> states;
}
public class FooUpdateDetailsInDTO extends FooDTO {
public List<SimpleDto> states;
}
公共类foodDetailsOutTo将FooDTO扩展为{
公开名单国家;
}
对于“编辑”,定义DTO如下:
public class FooDetailsOutDTO extends FooDTO {
public List<Integer> states;
}
public class FooUpdateDetailsInDTO extends FooDTO {
public List<SimpleDto> states;
}
public类foodupdatedetailsindto扩展FooDTO{
公开名单国家;
}
您可以为itemPOJO
使用注释和两个构造函数。如果数组1-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
}
使用的版本:写入dto
public class FooDTO {
public Integer id;
public String label;
public Double amount;
public List<Object> states;
}
public class FooDTO{
公共整数id;
公共字符串标签;
公共双倍金额;
公开名单国家;
}
在服务类中键入DTO并处理异常另一种重新建模方法:
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)
...
public class FooDTO{
公共整数id;
公共字符串标签;
公共双倍金额;
//不是空的!
公开名单国家;
//可为空!!
…列出国家标签;
//您应该确保“稳定/一致的索引/顺序”(相对于状态)
...
…并相应地将其用于“get”(单独访问标签)和“post”(省略标签;)
------------------------------------
更好的是:
Map<Integer, String> states; // !?
映射状态;/!?
您只需添加一个getter,它在SimpleDto中返回整数
使用简单java流添加一个getter,该getter在FooDTO
中返回List
,并使用DTO getter映射到整数
states.stream().map(SimpleDto::getValue).collect(Collectors.toList());
使用自定义反序列化程序是解决问题的一种方法
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;
}
}
整数列表的输入有效负载
您可以尝试重新设计整个体系结构。使用相关集合拆分主实体
为每个实体提供独立的服务来添加/删除/设置状态。通过这种方式,您可以轻松地为您的客户端提供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);
}
@Path(../foo)
@产生
公共接口服务{
//Foo本身上的CRUD方法,这些方法只处理Foo的属性
...
@得到
@路径(“/{fooId}”)
foodtofindbyid(@PathParam(“fooId”)intfooid);
//与状态相关的方法:
@得到
@路径(“/{fooId}/status”)
列出状态(@PathParam(“fooId”)int fooId);
@职位
@路径(“/{fooId}/status”)
void addStatus(@PathParam(“fooId”)int fooId,int statusId);
@删除
@路径(“{fooId}/status”)
void deleteStatus(@PathParam(“fooId”)int-fooId,int-statusId);
@放
@路径(“/状态”)
void setstatus(@PathParam(“fooId”)int fooId,List newstatus);
}
对于此解决方案,还有一些备选方案,我更愿意返回:
@GET
@Path("/{fooId}/status")
List<Integer> statuses(@PathParam("fooId") int fooId);
@GET
@路径(“/{fooId}/status”)
列出状态(@PathParam(“fooId”)int fooId);
而不是DTO的列表。然后将提供一个服务来获取所有状态及其名称,而无需连接到Foo:
public interface StatusService {
List<SimpleDto> statuses();
}
公共接口状态服务{
列出状态();
}
为了简化GUI组件的实现,您可以创建Rest服务,该服务将返回一个组合数据,就像在第二个FooDto版本中一样。它还将减少Rest调用的数量。但是,使用单独的方法直接处理一组项会有很大帮助。我不想使用额外的DTOI我不想使用空标签(null)@Youssef,这只是toString
方法实现,它以这种方式显示这些对象。Label属性设置为null
而不是“null”
String
。请尝试我的解决方案,您会看到的。我知道它有效,我在发布此问题之前尝试过它……但我正在寻找一个具有真正动态字段的更好的解决方案type@Youssef,您所说的实动态字段类型
是什么意思?您想如何区分整数
s和DT的列表O
?您可以创建列表
,一旦接收到整数
,对象将成为映射
。无法为整数
或DTO
动态映射它。您需要为此字段编写自定义反序列化程序。我的解决方案在这两种情况下为您提供相同的类型,您甚至可以混合使用pr使用DTO
进行模拟。我不想使用额外的DTO我不明白。只需将其设置为对象
并使用一个简单的实例
,为什么这么复杂?@Eugene yeah实例
用于基本类型,但对于我的实例
,我想我必须使用@manojramana?你知道什么实例吗是吗?你说你有
{
"states": [
1,2
]
}
@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);
public interface StatusService {
List<SimpleDto> statuses();
}