【问题标题】:Find line number of key in JSON在 JSON 中查找键的行号
【发布时间】:2022-01-09 17:35:03
【问题描述】:

给定一个 JSON 文件和一个键的路径,我需要能够获取存储值的行号。在我弄清楚如何处理最简单的情况之前,需要多行的值(例如数组)目前不在范围内。例如,在以下 JSON 中:

{                        # Line 1
    "foo": {             # Line 2
        "bar": [
            "value1",
            "value2"
        ],
        "bar2": 2        # Line 7
    },
    "bar": {
        "bar": [
            "value1",
            "value2"
        ],
        "bar2": 5
    }
}

在查找密钥路径 foo.bar2 时,我应该得到 7

假设原始文件的格式为JSON.pretty_generate,我有一个可行的解决方案:

parsed_json = File.read(file)
random_string = SecureRandom.uuid

parsed_json.bury(*path.split('.'), random_string)

JSON.pretty_generate(parsed_json)
    .split("\n")
    .take_while { |s| s.exclude? random_string }
    .count + 1

我在这里所做的是解析 JSON 文件,用随机字符串(在本例中为 UUID)替换现有值,将散列格式化为漂亮打印的 JSON,并找到存在随机字符串的行。此处使用的Hash.bury 方法按照https://bugs.ruby-lang.org/issues/11747 中的定义工作。

此解决方案运行良好(但尚未经过大量测试),但当原始文件未格式化为打印精美的 JSON 时,我正在努力使其工作。例如,以下文件将等同于上述文件:

{                                     # Line 1
    "foo": {                          # Line 2
        "bar": ["value1","value2"],  
        "bar2": 2                     # Line 4
    },
    "bar": {
        "bar": [
            "value1",
            "value2"
        ],
        "bar2": 5
    }
}

但是上面的算法仍然会返回7作为foo.bar2所在的行,虽然现在它在4行中。

有什么方法可以可靠地获取 JSON 文件中密钥所在的行号?

【问题讨论】:

  • 在漂亮打印的输入或输出上使用 grep。解析后,JSON 并没有真正的行号。不过,文件和输出可以。

标签: json ruby jsonparser


【解决方案1】:

这是我发现的最简单的方法,无需构建您自己的 JSON 解析器:将 每个 键条目替换为唯一的 UUID(别名),然后构建所有别名组合并找到从 @ 返回数据的组合987654321@电话

keys = path.split('.')
file_content = File.read(file_path).gsub('null', '1111')
aliases = {}

keys.each do |key|
  pattern = "\"#{key}\":"

  file_content.scan(pattern).each do
    alias_key = SecureRandom.uuid
    file_content.sub!(pattern, "\"#{alias_key}\":")

    aliases[key] ||= []
    aliases[key] << alias_key
  end
end

winner = aliases.values.flatten.combination(keys.size).find do |alias_keys|
  # nulls were gsubbed above to make this check work in edge case when path value is null
  JSON.parse(file_content).dig(*alias_keys).present?
end

file_content.split("\n").take_while { |line| line.exclude?(winner.last) }.count + 1

UPD:如果您的 foo.bar2 键的 JSON 值为 false,则上述 sn-p 将不起作用。你也应该gsub它或者让这个sn-p更智能

【讨论】:

    【解决方案2】:

    我有一个想法:使用相同的 uuid 技巧(顺便说一句,这很棒),并比较您的 json 和文件内容,但忽略每个空格。

    一旦你找到不同的字符,它应该是你想要的那一行。

    我写了一个似乎可以工作的程序,没有经过大量测试;)(我复制了 bury 的定义):

    require 'json'
    require 'readline'
    require 'securerandom'
    
    class Hash
      def bury *args
        if args.count < 2
          raise ArgumentError.new("2 or more arguments required")
        elsif args.count == 2
          self[args[0]] = args[1]
        else
          arg = args.shift
          self[arg] = {} unless self[arg]
          self[arg].bury(*args) unless args.empty?
        end
        self
      end
    end
    
    file_path = 'your/file/path.json'
    path = 'foo.bar2'
    
    file_content = File.read(file_path)
    parsed_json = JSON.parse(file_content)
    uuid = SecureRandom.uuid
    
    parsed_json.bury(*path.split('.'), uuid)
    
    compacted_json = JSON.pretty_generate(parsed_json).gsub(/\s/, '')
    @file_lines = File.readlines(file_path)
    
    index = 0
    loop do
      compact_line = @file_lines[index].gsub(/\s/, '')
      break if !compacted_json.start_with?(compact_line)
      index += 1
      compacted_json = compacted_json[compact_line.length..-1]
    end
    
    puts "line is #{index+1}"
    

    【讨论】:

      猜你喜欢
      • 2019-01-07
      • 2023-02-05
      • 1970-01-01
      • 2019-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 2016-08-21
      相关资源
      最近更新 更多