将列表<String> 解析为JSON
Parse List<String> as JSON
我正在阅读《真实世界软件开发》一书,当前章节是关于编写一个 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
并解析每一行并输出这个 class:
的一个对象
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
class 是不可变的并且不允许设置器。
我设法找到了一个解决方案,但它看起来很糟糕:
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"
}
]
}
您的问题有一些不同的解决方案:
- 您可以创建自己的TypeAdapter来解析列表
- 您可以使用 TypeTokens 在运行时告诉 Gson 您将加载什么(尽管这肯定会破坏字段
date
)
- 您可以创建自己的 JsonDeserializer
我个人会做第二个并为 LocalDate 创建自定义 TypeAdapter,它在注册后由 Gson 自动应用。
我使用了已接受的答案所描述的方法,但在 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);
}
}
我正在阅读《真实世界软件开发》一书,当前章节是关于编写一个 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
并解析每一行并输出这个 class:
的一个对象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
class 是不可变的并且不允许设置器。
我设法找到了一个解决方案,但它看起来很糟糕:
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"
}
]
}
您的问题有一些不同的解决方案:
- 您可以创建自己的TypeAdapter来解析列表
- 您可以使用 TypeTokens 在运行时告诉 Gson 您将加载什么(尽管这肯定会破坏字段
date
) - 您可以创建自己的 JsonDeserializer
我个人会做第二个并为 LocalDate 创建自定义 TypeAdapter,它在注册后由 Gson 自动应用。
我使用了已接受的答案所描述的方法,但在 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);
}
}