evans
evans

Reputation: 563

How to save the tree structure when parsing json?

My json:

{
    "catalogs": {
        "name": "catalog 1C",
        "children": [{
                "name": "Philips",
                "children": {}
            },
            {
                "name": "BenQ",
                "children": [{
                    "name": "Monitor",
                    "children": [{
                        "name": "19 inches",
                        "children": [{"name": "IPS", "children": {}}, 
                                     {"name": "TFT","children": {}}] 
                     },
                     {
                         "name": "24 inches",
                         "children": {} }]
                }]
            }
        ]
    }
}

Json have same keys and levels of nesting within each other.
сhildren can have many directories (for example IPS, TFT).

This is a menu where some items should be on the same level. I use gem ancestry

I wrote a recursive function (in the controller):

def call
  # file - inside the json to parse
  data_hash = JSON.parse(file)

  data_hash["catalogs"]["children"].each do |k|
    ancestry = Category.create_with(name: k["name"]).find_or_create_by!(uid_catalog: k["uid_catalog"])

    if k["children"].present?
      k["children"].each do |parent|
        parent["children"].each do |child|
          walk_tree(child, ancestry)
        end
      end
    end
  end
end

def walk_tree(root, ancestry)
  ancestry = ancestry.children.find_or_create_by!(name: root["name"])

  root["children"].each do |child|
    ancestry = ancestry.children.find_or_create_by!(name: child["name"])
    walk_tree(child["children"], ancestry) if child["children"].present?
  end
end

Currently the function outputs:

  - BenQ
    - 19 inches
      - IPS
        - TFT

I want to have it like this (missing one level Monitor):

  - BenQ
    - Monitor
       - 19 inches
         - IPS
         - TFT
       - 24 inches 

Upvotes: 0

Views: 428

Answers (1)

Ilya Konyukhov
Ilya Konyukhov

Reputation: 2791

The cause of the problem reveals after quick debugging. Take a look at this chunk of code. Please note my comments:

data_hash["catalogs"]["children"].each do |k|
  # k == { "name": "BenQ", "children": [...] }
  ancestry = Category.create_with(name: k["name"]).
    find_or_create_by!(uid_catalog: k["uid_catalog"])

  if k["children"].present?
    k["children"].each do |parent|
      # parent == { "name": "Monitor", "children": [...] }
      parent["children"].each do |child|
        # child == { "name": "19 inches", "children": [...] }
        walk_tree(child, ancestry)
      end
    end
  end
end

So you do not process parent as a separate node; instead your code immediately dives deeper into parent["children"]. That's why one level of nesting is missing.

I think you need to remove inner iterator, and rewrite it as:

data_hash["catalogs"]["children"].each do |k|
  ancestry = Category.create_with(name: k["name"]).
    find_or_create_by!(uid_catalog: k["uid_catalog"])

  if k["children"].present?
    k["children"].each do |child|
      walk_tree(child, ancestry)
    end
  end
end

Upvotes: 1

Related Questions