Rails 上的 Ruby:如何使用 JSON 路径访问(并保存到数据库)嵌套在 JSON 数组中的 objects/properties?

Ruby on Rails: How can I use JSONPath to access (and save to database) nested objects/properties within a JSON array?

我正在尝试从 JSON 对象数组向我的数据库播种数据。我有两个单独的数据库表 - 属性 和单位(属性 有很多单位)。我已经能够通过 API 请求 JSON 数据成功地为 属性 信息(属性 模型)执行此操作,然后将其播种到数据库但是 ValuationReport 属性在我的前端显示为字符串形式的 JSON 对象。

这是我正在使用的 JSON 数据的示例(出于演示目的,我将此示例限制为包含 3 个对象的数组):

[
  {
    "PublicationDate": "17/09/2019",
    "PropertyNumber": 2198231,
    "County": "CAVAN",
    "LocalAuthority": "CAVAN COUNTY COUNCIL",
    "Valuation": 15090,
    "Category": "OFFICE",
    "Uses": "OFFICE (OVER THE SHOP), -",
    "Address1": "77(1ST & 2ND FL) MAIN STREET",
    "Address2": "CAVAN",
    "Address3": "CO. CAVAN",
    "Address4": "",
    "Address5": "",
    "CarPark": 0,
    "Xitm": 641927.73,
    "Yitm": 804638.3,
    "ValuationReport": [
      {
        "Level": "1",
        "FloorUse": "RESTAURANT",
        "Area": 112.25,
        "NavPerM2": 80,
        "Nav": 8980
      },
      {
        "Level": "2",
        "FloorUse": "RESTAURANT",
        "Area": 98.57,
        "NavPerM2": 62,
        "Nav": 6111.34
      }
    ]
  },
  {
    "PublicationDate": "17/09/2019",
    "PropertyNumber": 1558322,
    "County": "CAVAN",
    "LocalAuthority": "CAVAN COUNTY COUNCIL",
    "Valuation": 10200,
    "Category": "OFFICE",
    "Uses": "OFFICE (OWN DOOR), -",
    "Address1": "23E MAIN STREET",
    "Address2": "CAVAN",
    "Address3": "CO. CAVAN",
    "Address4": "",
    "Address5": "",
    "CarPark": 0,
    "Xitm": 641941.19,
    "Yitm": 804875.56,
    "ValuationReport": [
      {
        "Level": "0",
        "FloorUse": "OFFICE(S)",
        "Area": 127.55,
        "NavPerM2": 80,
        "Nav": 10204
      }
    ]
  },
  {
    "PublicationDate": "17/09/2019",
    "PropertyNumber": 2116802,
    "County": "CAVAN",
    "LocalAuthority": "CAVAN COUNTY COUNCIL",
    "Valuation": 15140,
    "Category": "OFFICE",
    "Uses": "OFFICE (OWN DOOR), HERITAGE / INTERPRETATIVE CENTRE",
    "Address1": "LOCAL NO/MAP REF: 7AB",
    "Address2": "MULLAGH",
    "Address3": "BAILIEBORO CO. CAVAN",
    "Address4": "",
    "Address5": "",
    "CarPark": 0,
    "Xitm": 668656.19,
    "Yitm": 785281.05,
    "ValuationReport": [
      {
        "Level": "0",
        "FloorUse": "OFFICE(S)",
        "Area": 252.49,
        "NavPerM2": 60,
        "Nav": 15149.4
property_id: unit[property.id]
      }
    ]
  }
]

我遇到的困难是 ValuationReport 属性,它是 JSON 属性数组中每个元素的大小不同的数组。因此,某些属性将具有仅包含一个对象的 ValuationReport 数组,而其他属性将包含具有多个对象的 ValuationReport 对象。我想将每个 ValuationReport 数组中的每个嵌套对象保存到我的数据库(单元模型)中的一个单独单元中。

我已经使用 JSON路径 Gem 尝试访问所需的元素,但我做错了什么。

当我 运行 '$ rails db:seed' 时,现在 运行ning 直到一个点,我收到以下错误:

Creating property 2198231
Creating unit.
rails aborted!
TypeError: no implicit conversion of String into Integer
/db/seeds.rb:62:in `[]'
/db/seeds.rb:62:in `block (2 levels) in properties'
/db/seeds.rb:58:in `each'
/db/seeds.rb:58:in `block in properties'
/db/seeds.rb:36:in `each'
/db/seeds.rb:36:in `properties'
/db/seeds.rb:207:in `<top (required)>'
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:seed
(See full trace by running task with --trace)

这是我的 Gem 文件中的相关 Gem:

# REST Client for APIs
gem 'rest-client', '~> 2.1'

# JSONPath - for acessing values in nested JSON objects.
gem 'jsonpath', '~> 0.5.8'

我的数据库架构如下:

ActiveRecord::Schema.define(version: 2020_11_07_130018) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "properties", force: :cascade do |t|
    t.date "publication_date"
    t.string "property_number"
    t.string "county"
    t.string "local_authority"
    t.decimal "valuation"
    t.string "category"
    t.string "uses"
    t.string "address_1"
    t.string "address_2"
    t.string "address_3"
    t.string "address_4"
    t.string "address_5"
    t.decimal "car_park"
    t.decimal "xitm"
    t.decimal "yitm"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "units", force: :cascade do |t|
    t.string "level"
    t.string "floor_use"
    t.decimal "area"
    t.decimal "nav_per_m2"
    t.decimal "nav"
    t.bigint "property_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["property_id"], name: "index_units_on_property_id"
  end

  add_foreign_key "units", "properties"
end

我的 seeds.rb 文件如下。我尝试使用索引 (i) 来确保 ValuationReport 对象数组的循环为每个 属性 循环一次并创建每个 属性.[=19 中存在的所有单位=]

require 'rest-client'
require 'json'
require 'jsonpath'

# Define Method - API Request
def properties

    response = RestClient.get('https://api.valoff.ie/api/Property/GetProperties?Fields=*&LocalAuthority=CAVAN%20COUNTY%20COUNCIL&CategorySelected=OFFICE&Format=csv&Download=false')
    json = JSON.parse(response)

    json.each do |property|
        puts "Creating property #{property['PropertyNumber']}"

        Property.create!(

            publication_date: property['PublicationDate'],
            property_number: property['PropertyNumber'],
            county: property['County'],
            local_authority: property['LocalAuthority'],
            valuation: property['Valuation'],
            category: property['Category'],
            uses: property['Uses'],
            address_1: property['Address1'],
            address_2: property['Address2'],
            address_3: property['Address3'],
            address_4: property['Address4'],
            address_5: property['Address5'],
            car_park: property['CarPark'],
            xitm: property['Xitm'],
            yitm: property['Yitm'],
            units: 
                JsonPath.new('$.ValuationReport').on(property).each do |unit|
                    puts "Creating unit."
                    Unit.create!(
                        level: unit['$.Level'],
                        floor_use: unit['$.FloorUse'],
                        area: unit['$.Area'],
                        nav_per_m2: unit['$.NavPerM2'],
                        nav: unit['$.Nav'],
                        property_id: unit['property.id']
                    )
                end
        )
    end
end

# Call Method
properties

感谢您的宝贵时间和帮助!

在这种情况下,您不需要 JsonPath。我更新了你的脚本:

  • 删除了 JsonPath 以支持使用 property['ValuationReport']
  • 从 json 个键中删除了 $.
  • 首先你应该创建一个 属性,然后在 Units 中使用它的 ID(或者你可以在模型中设置 accepts_nested_attributes
require 'rest-client'
require 'json'
require 'jsonpath'

# Define Method - API Request
def properties

    response = RestClient.get('https://api.valoff.ie/api/Property/GetProperties?Fields=*&LocalAuthority=CAVAN%20COUNTY%20COUNCIL&CategorySelected=OFFICE&Format=csv&Download=false')
    json = JSON.parse(response)

    json.each do |property|
        puts "Creating property #{property['PropertyNumber']}"

        property_model = Property.create!(

            publication_date: property['PublicationDate'],
            property_number: property['PropertyNumber'],
            county: property['County'],
            local_authority: property['LocalAuthority'],
            valuation: property['Valuation'],
            category: property['Category'],
            uses: property['Uses'],
            address_1: property['Address1'],
            address_2: property['Address2'],
            address_3: property['Address3'],
            address_4: property['Address4'],
            address_5: property['Address5'],
            car_park: property['CarPark'],
            xitm: property['Xitm'],
            yitm: property['Yitm']
        )


        property['ValuationReport'].each do |unit|
          puts "Creating unit."
          property_model.units.create!(
            level: unit['Level'],
            floor_use: unit['FloorUse'],
            area: unit['Area'],
            nav_per_m2: unit['NavPerM2'],
            nav: unit['Nav']
          )
        end
    end
end

# Call Method
properties