【问题标题】:JSON decoding in PERL - Maintaining the original data typePERL 中的 JSON 解码 - 维护原始数据类型
【发布时间】:2015-04-22 17:06:13
【问题描述】:

我正在编写一个简单的 perl 脚本来从文件中读取 JSON 并将其插入 MongoDB。但我面临 json 解码的问题。 我的原始 json 中的所有非字符串值都在 decode_json 之后转换为对象类型。

输入 JSON(只有一部分,因为它是原始的,很大) -

{
   "_id": 2006010100000801089, 
   "show_image" : false,
   "event" : "publish",
   "publish_date" :1136091600,
   "data_version" : 1
}

插入到 MongoDB 的 JSON -

{
   "_id": NumberLong("2006010100000801089"), 
   "show_image" : BinData(0,"MA=="),
   "event" : "publish",
   "publish_date" :NumberLong(1136091600),
   "data_version" : NumberLong(1)
}

我正在为文档提供自定义 _id,我希望将其转换为 NumberLong 类型。正如您从上面的 JSON 中看到的那样,它按预期工作。但请注意 show_imagepublish_datedata_version 的其他非字符串值是如何转换为它的对象表示的。
有什么办法可以保留这些值的原始类型?

执行插入的 Perl 代码剪辑器 -

use MongoDB;
use MongoDB::OID;
use JSON;
use JSON::XS
while(my $record = <$source_file>) {
  my $record_decoded = decode_json($record);
  $db_collection->insert($record_decoded);
}

使用的 Perl 版本为 v5.18.2。

我查找了JSON::XS docs,但找不到执行此操作的方法。任何帮助表示赞赏。提前致谢!

我对 perl 很陌生。对不起,如果这是一个微不足道的问题。

【问题讨论】:

  • 关于“插入 MongoDB 的 JSON”,这不是 JSON
  • Re“有什么办法可以保留这些值的原始类型吗?”,我不确定你在问什么。我认为您想更改插入数据库的内容。如果是这样,您究竟想为您的示例插入什么?
  • 例如参考我的示例 JSON,我希望将 show_image 的值插入为 false 而不是 BinData(0,"MA==")publish_date 插入为 1136091600 而不是 NumberLong(1136091600)
  • 感谢@ikegami 解决我的问题。如果我不解码 JSON,我会收到异常 not a reference at /Library/Perl/5.18/darwin-thread-multi-2level/MongoDB/Collection.pm line 235
  • 那么那个方法就不能用来插入JSON了。为什么你说你想要 JSON?或者为什么将 JSON 传递给不接受 JSON 的方法?你没有道理。

标签: json perl mongodb


【解决方案1】:

我正在为文档提供自定义 _id,我希望将其转换为 NumberLong 类型。正如您从上面的 JSON 中看到的那样,它按预期工作。但请注意 show_image、publish_date 和 data_version 的其他非字符串值是如何转换为它的对象表示的。

从您的示例中,所有数据类型实际上都匹配,除了 show_image 的布尔值当前正在转换为二进制数据。

mongo shell 查询时,数字类型应显示为NumberLongNumberIntmongo shell 使用 JavaScript,它只有 single numeric typeNumber(64 位浮点)。像 NumberLong()NumberInt() 这样的 Shell 助手用于表示 MongoDB 的 BSON data types 中没有原生 JavaScript 等效项的值。

参考我的示例 JSON,我希望将 show_image 的值插入为 false 而不是 BinData(0,"MA==​​") 并将 publish_date 插入为 1136091600 而不是 NumberLong(1136091600)

虽然如果适合您的用例,可以将 publish_date 作为 Unixtime 插入,但您可能会发现改用 MongoDB 的 Date type 更有用。查询日期的便捷方法包括Date Aggregation Operators。仅供参考,日期字段将显示在带有 ISODate() 包装器的 mongo shell 中。

不过,show_image 的布尔值肯定需要帮助。

如果您使用Data::Dumper 来检查来自decode_json() 的结果,您将看到show_image 字段是一个祝福对象:

'show_image' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' )

为了在 MongoDB 中获得预期的 boolean 值,MongoDB 模块文档中推荐的方法是使用 boolean 模块(参见:MongoDB::DataTypes)。

我找不到 JSON 或 JSON::XS 的明显内置选项来支持将布尔值序列化为 JSON 模拟布尔值类以外的东西,但一种解决方案是使用 Data::Clean::Base 模块,它是一部分Data::Clean::JSON 分布。

示例 sn-p(不包括 MongoDB 设置):

use Data::Clean::Base;
use boolean;

my $cleanser = Data::Clean::Base->new(
    'JSON::XS::Boolean' => ['call_func', 'boolean::boolean'],
    'JSON::PP::Boolean' => ['call_func', 'boolean::boolean']
);

while (my $record = <$source_file>) {
    my $record_decoded = decode_json($record);
    $cleanser->clean_in_place($record_decoded);
    $db_collection->insert($record_decoded);
}

保存在 MongoDB 3.0.2 中的示例记录:

{
  "_id": NumberLong("2006010100000801089"),
  "event": "publish",
  "data_version": NumberLong("1"),
  "show_image": false,
  "publish_date": NumberLong("1136091600")
}

【讨论】:

  • 是的,这对我有用。非常感谢详细而精彩的解释。 NumberLong 是有道理的,我可以忍受。但是布尔值真的让我很困扰。我需要加载数百万条档案记录,不想冒任何风险。非常感谢!
【解决方案2】:

JSON 数据仅包含(双精度)数字、字符串和特殊值 truefalsenull。它们可以排列成数组或“对象”(散列)。

MongoDB 引擎正在将这些基本类型转换为更复杂的类型,但原始值在$record_decoded 引用的哈希中可用,就像这样

$record_decoded->{_id}
$record_decoded->{show_image}
$record_decoded->{event}
$record_decoded->{publish_date}
$record_decoded->{data_version}

这是你想要的吗?

【讨论】:

  • 例如参考我的示例 JSON,我希望将 show_image 的值插入为 false 而不是 BinData(0,"MA==")publish_date 插入为 1136091600 而不是 NumberLong(1136091600)。我不认为这是 MongoDB 转换,因为当我通过执行 mongoimport 直接插入相同的 JSON 时,一切都很好。即show_image 的值是false
【解决方案3】:

JSON::XS 中的object serialization 文档(尤其是allow_tags)可能会执行您想要的操作。但请注意,这不是标准 JSON 功能,仅适用于 JSON::XS。

【讨论】:

    猜你喜欢
    • 2012-09-19
    • 1970-01-01
    • 2016-01-21
    • 2019-09-07
    • 2018-06-20
    • 1970-01-01
    • 2020-07-19
    • 2012-11-27
    • 1970-01-01
    相关资源
    最近更新 更多