ParthS007
ParthS007

Reputation: 2689

Get the Parent key and the nested value in nested json

I have a nested json for a JSON schema like this:

{
  "config": {
    "x-permission": true
  },
  "deposit_schema": {
    "additionalProperties": false,
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
      "control_number": {
        "type": "string",
        "x-cap-permission": {
          "users": [
            "[email protected]"
          ]
        }
      },
      "initial": {
        "properties": {
          "status": {
            "x-permission": {
              "users": [
                "[email protected]"
              ]
            },
            "title": "Status",
            "type": "object",
            "properties": {
              "main_status": {
                "type": "string",
                "title": "Stage"
              }
            }
          },
          "gitlab_repo": {
            "description": "Add your repository",
            "items": {
              "properties": {
                "directory": {
                  "title": "Subdirectory",
                  "type": "string",
                  "x-permission": {
                    "users": [
                      "[email protected]",
                      "[email protected]"
                    ]
                  }
                },
                "gitlab": {
                  "title": "Gitlab",
                  "type": "string"
                }
              },
              "type": "object"
            },
            "title": "Gitlab Repository",
            "type": "array"
          },
        "title": "Initial Input",
        "type": "object"
      }
    },
    "title": "Test Analysis"
  }
}

The JSON is nested and I want to have the dict of x-permission fields with their parent_key like this:

{
 "control_number": {"users": ["[email protected]"]},
 "initial.properties.status": {"users": ["[email protected]"]},
 "initial.properties.gitlab_repo.items.properties.directory": {"users": [
                      "[email protected]",
                      "[email protected]"
                    ]}
}

I am trying to do implement recursive logic for every key in JSON like this:

def extract(obj, parent_key):
    """Recursively search for values of key in JSON tree."""
    for k, v in obj.items():
        key = parent_key + '.' + k
        if isinstance(v, dict):
            if v.get('x-permission'):

                return key, v.get('x-permission')
            elif v.get('properties'):
                return extract(v.get('properties'), key)
    return None, None


def collect_permission_info(object_):
    # _schema = _schema.deposit_schema.get('properties')
    _schema = object_ # above json
    x_cap_fields = {}

    for k in _schema:
        parent_key, permission_info = extract(_schema.get(k), k)
        if parent_key and permission_info:
            x_cap_fields.update({parent_key: permission_info})
    return x_cap_fields

I am getting empty dict now, what I am missing here?

Upvotes: 1

Views: 701

Answers (3)

ParthS007
ParthS007

Reputation: 2689

After reading through the comments and the answers. I got this solution working for my use case.

def parse_schema_permission_info(schema):
    x_fields = {}

    def extract_permission_field(field, parent_field):
        for field, value in field.items():
            if field == 'x-permission':
                x_fields.update({parent_field: value})
            if isinstance(value, dict):
                key = parent_field + '.' + field
                if value.get('x-permission'):
                    x_fields.update(
                        {key: value.get('x-permission')}
                    )
                extract_permission_field(value, key)

    for field in schema:
        extract_permission_field(schema.get(field), field)

    return x_fields

Upvotes: 0

David Rubin
David Rubin

Reputation: 196

A few issues I can spot:

  1. You use the parent_key directly in the recursive function. In a case when multiple properties exist in an object ("_experiment" has 2 properties), the path will be incorrect (e.g. _experiment.type.x-permission is constructed in second loop call). Use a new variable so that each subsequent for loop call uses the initial parent_key value
  2. The elif branch is never executed as the first branch has priority. It is a duplicate.
  3. The return value from the recursive execute(...) call is ignored. Anything you might find on deeper levels is therefore ignored
  4. Judging by your example json schema and the desired result, a recursive call on the "initial": {...} object should return multiple results. You would have to modify the extract(...) function to allow for multiple results instead of a single one
  5. You only check if an object contains a x-permission or a properties attribute. This ignores the desired result in the provided "initial" schema branch which contains x-permission nested inside a status and main_status branch. The easiest solution is to invoke a recursive call every time isinstance(v, dict) == true

Upvotes: 1

trincot
trincot

Reputation: 350252

You could use this generator of key/value tuples:

def collect_permission_info(schema):
    for key, child in schema.items():
        if isinstance(child, dict):
            if "x-permission" in child:
                yield key, child["x-permission"]
            if "properties" in child:
                for rest, value in collect_permission_info(child["properties"]):
                    yield key + "." + rest, value

Then call it like this:

result = dict(collect_permission_info(schema))

Upvotes: 2

Related Questions