将List<String>解析为JSON

3

我正在跟随《现实世界软件开发》这本书,当前章节是关于编写一个BankStatementCSVParser的,它可以读取如下文件:

30-01-2017,-100, Deliveroo
30-01-2017,-50, Tesco
01-02-2017,6000, Salary
02-02-2017,2000, Royalties
02-02-2017,-4000, Rent
03-02-2017,3000, Tesco
05-02-2017,-30, Cinema

并解析每一行并输出此类的对象:
public class BankTransaction {
    private final LocalDate date;
    private final double amount;
    private final String description;

    public BankTransaction(final LocalDate date, final double amount, 
           final String description) {
        this.date = date;
        this.amount = amount;
        this.description = description;
    }

    public LocalDate getDate() {
        return date;
    }

    public double getAmount() {
        return amount;
    }

    public String getDescription() {
        return description;
    }
}

这个很好用,CSV也很容易(特别是因为我一直在抄书...),问题出现在章节末尾,当我被要求自己实现BankStatementJSONParser时。

我已经尝试了GSON和Jackson,但由于我的BankTransaction类是不可变的并且不允许设置器,所以我无法真正获得任何示例。

我设法让一个解决方案工作了,但它看起来很可怕:

public List<BankTransaction> parseLinesFrom(List<String> lines) {
    Map<String, ?> map = new Gson().fromJson(String.join(" ", lines), Map.class);
    List<?> listOfTransactions = (List<?>) map.get("transactions");

    List<BankTransaction> bankTransactions = new ArrayList<>();
    listOfTransactions.forEach(rawJson -> {
        LinkedTreeMap<String, ?> javaJson = (LinkedTreeMap<String, ?>) rawJson;

        final LocalDate localDate = LocalDate.parse(javaJson.get("date").toString(), DATE_PATTERN);
        final double amount = Double.parseDouble(javaJson.get("amount").toString());
        final String description = javaJson.get("description").toString();
        BankTransaction newTransaction = new BankTransaction(localDate, amount, description);
        bankTransactions.add(newTransaction);
    });
    return bankTransactions;
}

感谢任何有关如何使这个过程更少丑陋的见解,也许问题出在我自己编写的 .json 文件上:

{
  "transactions": [
    {
      "date": "30-01-2017",
      "amount": -100,
      "description": "Deliveroo"
    },
    {
      "date": "01-02-2017",
      "amount": 6000,
      "description": "Salary"
    }
  ]
}

2
将构造函数用@JsonCreator进行注释,并使用@JsonProperty对其参数进行注释,这样是否可以解决您的问题?然后您就可以直接使用Jackson进行反序列化:objectMapper.readValue(input, List.class) - knittl
谢谢您的建议,它几乎起作用了,但似乎不喜欢日期字段,尝试添加一些@JsonFormat,但它一直抱怨非法反射。 - Xenofono
2个回答

2
您的问题有几种不同的解决方案:
  1. 您可以创建自己的 TypeAdapter 来解析列表
  2. 您可以使用 TypeToken 在运行时告诉 Gson 您将加载什么(尽管这肯定会破坏字段 date
  3. 您可以创建自己的 JsonDeserializer

我个人会选择第二种方法,并为 LocalDate 创建一个自定义 TypeAdapter,在注册后由 Gson 自动应用。


谢谢,我按照你说的用了Jackson。对我来说有点复杂,但现在感觉好像少了些魔法 :) - Xenofono

0

我使用了被接受的答案中描述的方法,但是在Jackson而不是GSON中通过覆盖Jackson ObjectMapper并在构造函数中接受DateTimeFormatter来实现:

public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper(DateTimeFormatter pattern){
    SimpleModule customModule = new SimpleModule("LocalDateMapper");
    customModule.addSerializer(LocalDate.class, new JsonSerializer<LocalDate>() {
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            ToStringSerializer.instance.serialize(value, gen, serializers);
        }
    });
    customModule.addDeserializer(LocalDate.class, new JsonDeserializer<LocalDate>() {
        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return LocalDate.parse(p.getText(), pattern);
        }
    });

    registerModule(customModule);

}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接