Общий способ разбора JSON - PullRequest
0 голосов
/ 17 октября 2018

У меня есть пример JSON

{
    "data":"some string data",
    "amount":200,
    "amountCurrencyList":[
        {"value":4000.0,"currency":"USD"}, 
        {"value":100.0,"currency":"GBP"}
    ]
}

и метод, который в настоящее время анализирует его в поле карты базового объекта

public void buildDetailsFromJson(String details) {
    if (details != null) {
        TypeReference<HashMap<String, Object>> mapTypeReference = new TypeReference<HashMap<String, Object>>() {
        };
        ObjectMapper mapper = new ObjectMapper();
        try {
            mapper.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
            mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
            detailsMap = mapper.readValue(details, mapTypeReference);
        } catch (IOException e) {
            log.error("Exception during JSON {} parsing! {}", details, e.getMessage());
        }
    }
}

Структура JSON может быть изменена.Идея состоит в том, чтобы иметь отдельный метод, который в идеале легко извлекал бы необходимый параметр, например map.get(key_name)

, например

public void setUpFieldsFromMap() {
    HashMap<String, Object> map = super.detailsMap;
    this.amountCurrencyList = (ArrayList<MoneyValue>) map.get("amountCurrencyList");
    if (isNull(amountCurrencyList)) {
        throw new InvalidOrMissingParametersException("Exception during JSON parsing! Critical data is missing in DetailsMap - " + map.toString());
    }
}

Таким образом, получая объект List по ключу и приводя его к нужному параметру.НО, когда я пытаюсь работать с List<MoneyValue>

System.out.println(detailsObj.getAmountCurrencyList().get(0).getValue());

, я получаю

Exception: java.util.LinkedHashMap cannot be cast to MoneyValue

Возможно ли на самом деле добиться того, чего я хочу, без жесткого кодирования TypeReference с такими точными параметрами, какTypeReference<HashMap<String, List<MoneyValue>>>?

UPD

public class MoneyValue {
@NotNull
private BigDecimal value;
@NotNull
private String currency;

Класс EventDetails

public class SomeEventDetails extends BaseEventDetails implements EventDetails {

private ArrayList<MoneyValue> amountCurrencyList;

@Override
public void setUpFieldsFromMap() {
    HashMap<String, Object> map = super.detailsMap;
    this.amountCurrencyList = (ArrayList<MoneyValue>) map.get("amountCurrencyList");
    if (isNull(amountCurrencyList)) {
        throw new InvalidOrMissingParametersException("Exception during JSON parsing! Critical data is missing in DetailsMap - " + map.toString());
    }
}

}

1 Ответ

0 голосов
/ 17 октября 2018

Если набор свойств (я имею в виду структуру входных данных) не поддается изменению, мы можем представить его как класс POJO.Пусть это будет DetailsData:

public class DetailsData {
  private String data;
  private BigDecimal amount;
  private List<MoneyValue> amountCurrencyList;

  /*getters, setters, constructors*/

  public DetailsData() {
  }

  @JsonProperty("amountCurrencyList")
  private void deserializeMoneyValue(List<Map<String, Object>> data) {
    if (Objects.isNull(amountCurrencyList)) {
        amountCurrencyList = new ArrayList<>();
    } else {
        amountCurrencyList.clear();
    }        
    MoneyValue moneyValue;
    for (Map<String, Object> item : data) {
      moneyValue = new MoneyValue(getValue(item.get("value")),
          (String) item.get("currency"));
      amountCurrencyList.add(moneyValue);
    }
  }

  private BigDecimal getValue(Object value) {
    BigDecimal result = null;
    if (value != null) {
      if (value instanceof BigDecimal) {
        result = (BigDecimal) value;
      } else if (value instanceof BigInteger) {
        result = new BigDecimal((BigInteger) value);
      } else if (value instanceof Number) {
        result = new BigDecimal(((Number) value).doubleValue());
      } else {
        throw new ClassCastException("Invalid value");
      }
    }
    return result;
  }
}

Как вы видите, здесь есть метод deserializeMoneyValue, аннотированный как свойство JSON.Таким образом, для ObjectMapper он будет служить в качестве пользовательского десериализатора.Метод getValue необходим, потому что Джексон работает таким образом, что значение будет десериализовано в классе, который удовлетворяет размеру значения.например, если значение = "100", оно будет десериализовано как Integer, если значение = "100.0" - как Double и так далее.На самом деле этот метод вы можете сделать static и перейти в некоторый класс утилит.Подводя итог, вы можете изменить buildDetailsFromJson следующим образом:

public void buildDetailsFromJson(String details) {
    if (details != null) {        
        ObjectMapper mapper = new ObjectMapper();
        try {
            mapper.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
            mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
            // assuming the eventDetails is an instance of DetailsData
            eventDetails = mapper.readValue(details, DetailsData.class);
        } catch (IOException e) {
            log.error("Exception during JSON {} parsing! {}", details, e.getMessage());
        }
    }
}

И класс EventDetail:

public class SomeEventDetails extends BaseEventDetails implements EventDetails {

    private List<MoneyValue> amountCurrencyList;

    @Override
    public void setUpFieldsFromMap() {
        this.amountCurrencyList = super.eventDetails.getAmountCurrencyList();
        if (isNull(amountCurrencyList)) {
            throw new InvalidOrMissingParametersException("Exception during JSON parsing! Critical data is missing in DetailsMap - " + super.eventDetails.toString());
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...