json - 如何递归搜索与给定模式匹配的所有节点的 JSON 文件并将 JSON 'path' 返回到节点及其值?

标签 json perl data-structures

假设我有 this文本文件中的 JSON:

{"widget": {
    "debug": "on",
    "window": {
        "title": "Sample Konfabulator Widget",
        "name": "main_window",
        "width": 500,
        "height": 500
    },
    "image": { 
        "src": "Images/Sun.png",
        "name": "sun1",
        "hOffset": 250,
        "vOffset": 250,
        "alignment": "center"
    },
    "text": {
        "data": "Click Here",
        "size": 36,
        "style": "bold",
        "name": "text1",
        "hOffset": 250,
        "vOffset": 100,
        "alignment": "center",
        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
    }
}}
使用 Perl,我已将文件读入一个名为 $json_obj 的 JSON 对象。使用 JSON::XS。
如何搜索 $json_obj对于名为 name 的所有节点并返回/打印以下内容作为结果/输出:
widget->window->name: main_window
widget->image->name: sun1
widget->text->name: text1
笔记:
  • 与搜索词匹配的节点名称可以出现在树的任何级别
  • 搜索词可以是纯文本或正则表达式
  • 我希望能够提供我自己的分支分隔符来覆盖默认值,例如 ->
  • 示例 / (为简单起见,我将把它放在 perl $variable 中)

  • 我希望能够在我的搜索中指定多个节点级别,以便指定 path匹配,例如:指定 id/colour将返回包含名为 id 的节点的所有路径这也是一个父节点,其子节点名为 colour
  • 在结果值周围显示双引号是可选的
  • 我希望能够搜索多种模式,例如/(name|alignment)/对于“查找所有名为 namealignment
  • 的节点

    在上面最后一个注释中显示搜索结果的示例:
    widget->window->name: main_window
    widget->image->name: sun1
    widget->image->alignment: center
    widget->text->name: text1
    widget->text->alignment: center
    
    由于 JSON 主要只是文本,我还不确定使用 JSON::XS 的好处,所以欢迎任何关于为什么这是更好或更坏的建议。
    不用说它需要递归才能搜索n任意级别的深度。
    这是我到目前为止所拥有的,但我只是其中的一部分:
    #!/usr/bin/perl
    
    use 5.14.0;
    use warnings;
    use strict;
    use IO::File;
    use JSON::XS;
    
    my $jsonfile = '/home/usr/filename.json';
    my $jsonpath = 'image/src'; # example search path
    my $pathsep = '/'; # for displaying results
    
    my $fh = IO::File->new("$jsonfile", "r");
    my $jsontext = join('',$fh->getlines());
    $fh->close();
    
    my $jsonobj = JSON::XS->new->utf8->pretty;
    
    if (defined $jsonpath) {
        my $perltext = $jsonobj->decode($jsontext); # is this correct?
        recurse_tree($perltext);
    } else {
        # print file to STDOUT
        say $jsontext;
    }
    
    sub recurse_tree {
        my $hash = shift @_;
        foreach my $key (sort keys %{$hash}) {
            if ($key eq $jsonpath) {
                say "$key = %{$hash}{$key} \n"; # example output
            }
            if (ref $hash->{$key} eq 'HASH' ||
                ref $hash->{$key} eq 'ARRAY') {
                recurse_tree($hash->{$key});
            }
        }
    }
    
    exit;
    
    上述脚本的预期结果将是:
    widget/image/src: Images/Sun.png
    

    最佳答案

    一旦 JSON 被解码,就会有一个复杂的(嵌套的)Perl 数据结构供您搜索,而您展示的代码正是针对该结构的。
    但是,有一些图书馆可以提供帮助;要么完全完成这项工作,要么提供完整的、工作的和经过测试的代码,您可以根据具体需求进行微调。
    模块Data::Leaf::Walker似乎合适。一个简单的例子

    use warnings;
    use strict;
    use feature 'say';
    
    use Data::Dump qw(dd);
    use JSON;
    use List::Util qw(any);
    
    use Data::Leaf::Walker;
    
    my $file = shift // 'data.json';                       # provided data sample
    
    my $json_data = do { local (@ARGV, $/) = $file; <> };  # read into a string
    chomp $json_data;
    
    my $ds = decode_json $json_data;
    dd $ds; say '';                   # show decoded data
        
    my $walker = Data::Leaf::Walker->new($ds);
    
    my $sep = '->';
    while ( my ($key_path, $value) = $walker->each ) { 
        my @keys_in_path = @$key_path;
        if (any { $_ eq 'name' } @keys_in_path) {          # selection criteria
            say join($sep, @keys_in_path), " => $value" 
        }   
    }
    
    这个“walker”遍历数据结构,保留每个叶子的键列表。这就是使该模块特别适合您的任务的原因,并且与许多其他模块相比,其目的简单。请参阅文档。
    以上打印,用于问题中提供的示例数据
    小部件->窗口->名称 => main_window
    小部件->文本->名称=> text1
    小部件->图像->名称 => sun1

    在上面的代码中选择键路径的标准的实现相当简单,因为它检查 'name'路径中的任何位置,一次,然后打印整个路径。虽然问题没有指定如何处理路径中较早的匹配项或多个匹配项,但这可以调整,因为我们始终拥有完整路径。
    您的愿望 list 的其余部分也很容易实现。细读List::UtilList::MoreUtils寻求阵列分析的帮助。
    Data::Traverse 是另一个模块,它是满足可能的特定需求的一个很好的起点。 .特别简单,在70-odd lines of code ,所以很容易定制。

    关于json - 如何递归搜索与给定模式匹配的所有节点的 JSON 文件并将 JSON 'path' 返回到节点及其值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66287984/

    相关文章:

    javascript - 回调排序函数按元音数量对数组进行排序

    c - 如何获取 C 中 union 内结构的大小?

    delphi - 访问存储在另一个单元Delphi中的数据

    json - Spring集成,如何使用@Transformer从/到JSON转换?

    perl - 是否有用于连接 Google Contacts API 的 Perl 模块?

    regex - 如果在语句中找到单个单词,perl 正则表达式用于打印整个语句

    perl - 基于 Moose 的 Web 框架

    javascript - 如何访问所有嵌套的关联数组元素?

    javascript - 使用javascript从日期和时间字符串到dd/mm/yyyy的日期转换

    android - Gson toJson 将 "\n"添加到我的属性(property)的末尾