Java Jackson如何将对象数组转换为类中的平面属性取决于值
我有以下来自远程服务的Java Jackson如何将对象数组转换为类中的平面属性取决于值,java,json,jackson,Java,Json,Jackson,我有以下来自远程服务的json对象: { "accumulators": [ { "balance": "100", "name": "SMS", "units": "International SMS" }, { "balance": "100", "name": "VOICE", "units": "minutes" }, { "balance": "50",
json
对象:
{
"accumulators": [
{
"balance": "100",
"name": "SMS",
"units": "International SMS"
},
{
"balance": "100",
"name": "VOICE",
"units": "minutes"
},
{
"balance": "50",
"name": "MMS",
"units": "MMS"
}
]
}
我想将其转换为以下类取决于该数组中对象的值,因此如果它是“name”:“MMS”
,则该值必须设置为累加器到MMS的值代码>:
public class BaseDTO {
private AccumulatorDTO messages;
private AccumulatorDTO minutes;
private AccumulatorDTO MMS;
// setters and geters
}
public class AccumulatorDTO {
private int balance;
private String name;
private String units;
// setters and geters
}
知道如何使用Jackson注释或自定义反序列化程序来实现吗
我可以这样做:
AccumulatorDTO[] accumulators = (new ObjectMapper()).convertValue(response.asJson().get("accumulators"), AccumulatorDTO[].class);
然后对数组进行迭代并设置每个属性,但对我的项目结构来说非常困难,我正在寻找一个更好的通用解决方案(对所有远程服务器使用1种方法,反序列化最好在DTO内部,不知何故,我在前端和后端之间做一些包装层).考虑使用反射,并根据JSON中的name
字段为BaseDTO
字段命名。使用@JsonCreator
带注释的构造函数满足了“反序列化更好地位于DTO内部”的要求。例如
然后像这样反序列化:
BaseDTO accumulators = new ObjectMapper().readValue(response.asJson(), BaseDTO.class);
这将根据数组元素及其名称初始化BaseDTO字段。如果一个字段不能与数组元素匹配并且抛出异常,它将允许该字段为null
Jackson没有一个注释来完成你想做的事情。我为你的BaseDTO类创建了这个Jackson自定义反序列化程序,满足你的需求。它寻找“balance”属性,当它找到时,它知道下面是“name”和“units”,所以它接受它们。然后它打开“name”属性,并将当前AccumeratorTo设置为BaseDTO类的右侧字段
public class CustomDeserializer extends StdDeserializer<BaseDTO>{
/**
*
*/
private static final long serialVersionUID = 1L;
public CustomDeserializer(Class<BaseDTO> t) {
super(t);
}
@Override
public BaseDTO deserialize(JsonParser jp, DeserializationContext dc)
throws IOException, JsonProcessingException {
BaseDTO bd = new BaseDTO();
JsonToken currentToken = null;
while ((currentToken = jp.nextValue()) != null) {
if (jp.getCurrentName() != null && jp.getCurrentName().equals("balance"))
{
System.out.println(jp.getCurrentName());
AccumulatorDTO adto = new AccumulatorDTO();
adto.setBalance(Integer.parseInt(jp.getValueAsString()));
currentToken = jp.nextValue();
adto.setName(jp.getValueAsString());
currentToken = jp.nextValue();
adto.setUnits(jp.getValueAsString());
switch (adto.getName().toLowerCase())
{
case "sms":
bd.setMessages(adto);
break;
case "voice":
bd.setMinutes(adto);
break;
case "mms":
bd.setMMS(adto);
break;
}
}
}
return bd;
}
}
我得到了下面的输出,看起来还可以,因为每个累加器都与正确的属性相关联
--- JSON to JAVA ---
BaseDTO [messages=AccumulatorDTO [balance=100, name=SMS, units=International SMS], minutes=AccumulatorDTO [balance=100, name=VOICE, units=minutes], MMS=AccumulatorDTO [balance=50, name=MMS, units=MMS]]
@Manos Nikolaidis提供的答案帮助我编写了真正的答案,他的答案一开始就很好,因为有些值包含空格或非标准值,所以我创建了一个映射来映射JSON和类上的字段:
@JsonCreator
public AccountDTO(@JsonProperty("accumulators") final AccumulatorDTO[] accumulators) {
HashMap<String, String> accumulatorsMap = new HashMap<>();
// key is value from JSON, value is field name of class
accumulatorsMap.put("intl sms", "internationalSMS");
accumulatorsMap.put("voice", "minutes");
accumulatorsMap.put("mms", "MMS");
accumulatorsMap.put("voicemessage", "voiceMessages");
accumulatorsMap.put("message", "messages");
for (AccumulatorDTO accumulator : accumulators) {
String fieldName = accumulator.getName().toLowerCase();
try {
Field field = getClass().getDeclaredField(accumulatorsMap.get(fieldName));
field.set(this, accumulator);
} catch (NoSuchFieldException | IllegalAccessException ignored) {
}
}
}
@JsonCreator
公共帐户到(@JsonProperty(“累加器”)最终累加器到[]累加器){
HashMap累加器映射=新的HashMap();
//键是来自JSON的值,值是类的字段名
累计器映射。put(“国际短信”、“国际短信”);
累加器map.put(“语音”、“分钟”);
累加器映射放置(“mms”、“mms”);
累加器map.put(“语音信息”、“语音信息”);
累加器映射放置(“消息”、“消息”);
用于(蓄能器至蓄能器:蓄能器){
String fieldName=累加器.getName().toLowerCase();
试一试{
Field Field=getClass().getDeclaredField(累加器映射.get(fieldName));
设置字段(此字段为累加器);
}捕获(忽略NoSuchFieldException | IllegalacessException){
}
}
}
您的回答帮助了我,看起来很干净,谢谢。顺便说一句,看看我的答案,你的答案的编辑版本,如果你有什么建议让我知道:)
--- JSON to JAVA ---
BaseDTO [messages=AccumulatorDTO [balance=100, name=SMS, units=International SMS], minutes=AccumulatorDTO [balance=100, name=VOICE, units=minutes], MMS=AccumulatorDTO [balance=50, name=MMS, units=MMS]]
@JsonCreator
public AccountDTO(@JsonProperty("accumulators") final AccumulatorDTO[] accumulators) {
HashMap<String, String> accumulatorsMap = new HashMap<>();
// key is value from JSON, value is field name of class
accumulatorsMap.put("intl sms", "internationalSMS");
accumulatorsMap.put("voice", "minutes");
accumulatorsMap.put("mms", "MMS");
accumulatorsMap.put("voicemessage", "voiceMessages");
accumulatorsMap.put("message", "messages");
for (AccumulatorDTO accumulator : accumulators) {
String fieldName = accumulator.getName().toLowerCase();
try {
Field field = getClass().getDeclaredField(accumulatorsMap.get(fieldName));
field.set(this, accumulator);
} catch (NoSuchFieldException | IllegalAccessException ignored) {
}
}
}