我有来自远程服务的以下json
对象:
{
"accumulators": [
{
"balance": "100",
"name": "SMS",
"units": "International SMS"
},
{
"balance": "100",
"name": "VOICE",
"units": "minutes"
},
{
"balance": "50",
"name": "MMS",
"units": "MMS"
}
]
}
我将地图转换为以下类的魔杖取决于该数组中对象的值,因此如果它是"name": "MMS"
,那么该值必须设置为AccumulatorDTO 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不知何故,我在前端和后端之间做了一些包装层。)
答案 0 :(得分:1)
考虑使用反射并根据JSON中的BaseDTO
字段命名name
字段。使用@JsonCreator
带注释的构造函数可以更好地满足"反序列化的要求,以某种方式进入DTO" 。 E.g。
class BaseDTO {
private AccumulatorDTO sms;
private AccumulatorDTO voice;
private AccumulatorDTO mms;
@JsonCreator
public BaseDTO(@JsonProperty("accumulators") final AccumulatorDTO[] accumulators) {
for (AccumulatorDTO accumulator : accumulators) {
String fieldName = accumulator.getName().toLowerCase();
try {
Field field = getClass().getDeclaredField(fieldName);
field.set(this, accumulator);
} catch (NoSuchFieldException | IllegalAccessException ignored) {
}
}
}
}
并像这样反序列化:
BaseDTO accumulators = new ObjectMapper().readValue(response.asJson(), BaseDTO.class);
这将根据数组元素及其名称初始化BaseDTO字段。如果字段不能与数组元素匹配并且抛出异常,它将使该字段为空。
杰克逊没有注释来做你想要的AFAIK。
答案 1 :(得分:1)
@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) {
}
}
}
答案 2 :(得分:0)
我为您的BaseDTO课程创建了这个杰克逊定制解串器,满足您的要求。它寻找“平衡”属性,当它发现它知道以下是“名称”和“单位”所以它需要它们。然后它打开“name”属性,并将当前的AccumulatorDTO设置为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;
}
}
我用这个简单的程序测试了它:
public class JsonArrayExampleApplication {
public static void main(String[] args) {
//
String json = "{\"accumulators\": [{\"balance\": \"100\",\"name\": \"SMS\",\"units\": \"International SMS\"" +
"},{\"balance\": \"100\",\"name\": \"VOICE\",\"units\": \"minutes\"},{\"balance\": \"50\",\"name\": \"MMS\"," +
"\"units\": \"MMS\"}]}";
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("MyModule");
mod.addDeserializer(BaseDTO.class, new CustomDeserializer(BaseDTO.class));
mapper.registerModule(mod);
BaseDTO bdto = null;
try {
bdto = mapper.readValue(json, BaseDTO.class);
}
catch (IOException e) {
e.printStackTrace();
}
System.out.println("\n--- JSON to JAVA ---\n" + bdto);
}
}
我得到了以下似乎没问题的输出,因为每个AccumulatorDTO都与正确的属性相关联。
--- 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]]