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) {
        }
    }
}