2

I am fetching from an API and getting a JSON response like so:

 {
      "features": {
        "due_dates": {
          "enabled": true,
          "start_date": false,
          "remap_due_dates": true,
          "remap_closed_due_date": false
        },
        "sprints": {
          "enabled": false
        },
        "points": {
          "enabled": false
        },
        "custom_items": {
          "enabled": false
        },
        "tags": {
          "enabled": true
        },
        "time_estimates": {
          "enabled": true
        },
        "checklists": {
          "enabled": true
        },
        "zoom": {
          "enabled": false
        },
        "milestones": {
          "enabled": false
        },
        "custom_fields": {
          "enabled": true
        },
        "remap_dependencies": {
          "enabled": true
        },
        "dependency_warning": {
          "enabled": true
        },
        "multiple_assignees": {
          "enabled": true
        },
        "portfolios": {
          "enabled": true
        },
        "emails": {
          "enabled": true
        }
      }

I have a class setup like so:

class Features(BaseModel):
    multiple_assignees: bool = False
    start_date: bool = False
    remap_due_dates: bool = False
    remap_closed_due_date: bool = False
    time_tracking: bool = False
    tags: bool = False
    time_estimates: bool = False
    checklists: bool = False
    custom_fields: bool = False
    remap_dependencies: bool = False
    dependency_warning: bool = False
    portfolios: bool = False
    points: bool = False
    custom_items: bool = False
    zoom: bool = False
    milestones: bool = False
    emails: bool = False

What I would like to do is unpack this JSON into the features object and use the value from the "enabled" key for each corresponding attribute. I want to do it this way to avoid having to set up a class for sprints, points, custom_items, etc. Is it possible to just get the nested "enabled" bool values and skip over setting up a class for each?

Zach Johnson
  • 2,047
  • 6
  • 24
  • 40

1 Answers1

1

Unfortunately I don't think there's a very simple way of doing this. I guess most straightforward way would be to use a root validator as mentioned in this issue, so that we can unwrap fields using our own desired logic. Something like this should work for that:

import json

from pydantic import BaseModel, root_validator


class Features(BaseModel):
    multiple_assignees: bool = False
    start_date: bool = False
    remap_due_dates: bool = False
    remap_closed_due_date: bool = False
    time_tracking: bool = False
    tags: bool = False
    time_estimates: bool = False
    checklists: bool = False
    custom_fields: bool = False
    remap_dependencies: bool = False
    dependency_warning: bool = False
    portfolios: bool = False
    points: bool = False
    custom_items: bool = False
    zoom: bool = False
    milestones: bool = False
    emails: bool = False

    @root_validator(pre=True)
    def extract_features(cls, v):
        # Unwrap features and all 'enabled' values for each field within
        res = {k: v['enabled'] if isinstance(v, dict) and 'enabled' in v else v
               for k, v in v['features'].items()}
        # Map due date fields which are a bit different
        due_dates = v['features'].get('due_dates')
        if due_dates:
            for field in ('start_date',
                          'remap_due_dates',
                          'remap_closed_due_date'):
                res[field] = due_dates.get(field, False)

        return res


string = """
 {
      "features": {
        "due_dates": {
          "enabled": true,
          "start_date": false,
          "remap_due_dates": true,
          "remap_closed_due_date": false
        },
        "sprints": {
          "enabled": false
        },
        "points": {
          "enabled": false
        },
        "custom_items": {
          "enabled": false
        },
        "tags": {
          "enabled": true
        },
        "time_estimates": {
          "enabled": true
        },
        "checklists": {
          "enabled": true
        },
        "zoom": {
          "enabled": false
        },
        "milestones": {
          "enabled": false
        },
        "custom_fields": {
          "enabled": true
        },
        "remap_dependencies": {
          "enabled": true
        },
        "dependency_warning": {
          "enabled": true
        },
        "multiple_assignees": {
          "enabled": true
        },
        "portfolios": {
          "enabled": true
        },
        "emails": {
          "enabled": true
        }
    }
}
"""

d = json.loads(string)

x = Features(**d)
print(x)
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53