MongoDB:尝试从 JSON 中读取 Long 会导致 java.lang.Integer 无法转换为 java.lang.Long

Ale*_*exM 5 java json mongodb mongodb-java

我有一个代码可以从 MongoDB 读取特定格式的数据。我需要测试一下。

为此,我使用要测试的数据创建了一个 JSON:

{
  "id": ObjectId("57552e32e4b0839ede67e0af"),
  "serial" : 574000690,
  "startDate" : ISODate("2016-08-22T23:01:56.000Z"),
  "endDate" : ISODate("2016-10-22T22:01:56.000Z"),
  "reason": ""
}
Run Code Online (Sandbox Code Playgroud)

这是应该创建的对象:

public static class MyObject implements Serializable{
    private String id;
    private long serial;
    private Date startDate;
    private Date endDate;
    private String reason;
}
Run Code Online (Sandbox Code Playgroud)

我有一个读取 JSON 文件并创建 Mongo 文档并写入数据库的代码:

List<Document> docs = dirAsDbObjects(dir + File.separator + 
subDir.getName()).collect(Collectors.toList());

docs.forEach(docManipulator);
docs.forEach(doc -> doc.putIfAbsent("_id", new ObjectId()));

ret.addAll(docs);

MongoDatabase db = mongoClient.getDatabase(dbName);
MongoCollection<Document> coll = db.getCollection(subDir.getName());

List<InsertOneModel<Document>> inserts = docs.stream().map(InsertOneModel::new).collect(Collectors.toList());
coll.bulkWrite(inserts);
Run Code Online (Sandbox Code Playgroud)

写入数据后,有一段代码会尝试从 MongoDB 读取数据并将其填充到MyObject实例中:

    public MyObject(Document doc) {
        id = doc.getObjectId(DBConstants.ID).toString();
        serial = doc.getLong(DBConstants.SERIAL);
        startDate = doc.getDate("startDate");
        reason = doc.getString("source");
    }
Run Code Online (Sandbox Code Playgroud)

在错误消息上失败:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
Run Code Online (Sandbox Code Playgroud)

因为行serial = doc.getLong(DBConstants.SERIAL);。它基本上从我的 JSON 中获取数字为“整数”,因此无法读取它那么长时间。

我尝试了以下行,并且有效:

Long.parseLong(doc.get(DBConstants.SERIAL).toString())
Run Code Online (Sandbox Code Playgroud)

但这是最好的解决方案吗?请问.toString()总是让我一个数字的字符串表示?有没有办法在 JSON 中保留一个数字,使其被读取为Long

更新:

@glytching 给出了一个很好的答案!

我还发现了另一种方法,显然,如果您将 JSON 的数字元素包装在NumberLong其中,它将被解析为Long从 Mongo 文档翻译时。

因此,使用旧代码,我能够通过像这样更改我的 JSON 来使其工作:

{
  "id": ObjectId("57552e32e4b0839ede67e0af"),
  "serial" : NumberLong(574000690),
  "startDate" : ISODate("2016-08-22T23:01:56.000Z"),
  "endDate" : ISODate("2016-10-22T22:01:56.000Z"),
  "reason": ""
}
Run Code Online (Sandbox Code Playgroud)

gly*_*ing 6

Mongo Java 驱动程序已确定 的值serial可以“适合” INT32,因此将其视为此类。当您调用时,doc.getLong()您要求驱动程序将其 Integer 转换为 Long,因此类转换为异常。如果,例如,价值serial2147483648(即最大整数值+ 1),然后蒙戈Java驱动程序会认为这是一个INT64,然后就可以放心地调用doc.getLong()

因此,由于 (a) 您Long在类模型中将此属性建模为 a并且 (b) 并非此属性的每个持久值都需要存储为INT64... .

如何?好吧,只要serial属性作为某种数字(例如INT32, INT64)持久化,那么这个调用......

doc.get(DBConstants.SERIAL)
Run Code Online (Sandbox Code Playgroud)

...将始终返回一个对象,该对象是其某个子类,java.lang.Number因此强制转换为Number和使用longValue()将起作用。

例如:

serial = ((Number) doc.get(DBConstants.SERIAL)).longValue()
Run Code Online (Sandbox Code Playgroud)