| # Copyright 2019 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import re |
| |
| from recipe_engine import recipe_api |
| |
| |
| class JsonUtilApi(recipe_api.RecipeApi): |
| """JsonUtilApi contains JSON-related utilities.""" |
| |
| def permissive_loads(self, text): |
| """Extracts JSON from supplied text. |
| |
| The parsing is somewhat more permissive than the actual JSON spec; for |
| example, commas are allowed after the last elements of arrays and |
| objects. |
| |
| Args: |
| text (str): Text containing a JSON string. |
| |
| Returns: |
| A JSON object representing the extracted JSON string from the text. |
| """ |
| # Strip any comma after the last element of an object or array. |
| text = re.sub(r",(?=\s*[}\]])", "", text) |
| try: |
| return self.m.json.loads(text) |
| except ValueError as e: |
| raise self.m.step.StepFailure("failed to load JSON: %s" % str(e)) |
| |
| def retrieve_nested_field(self, json_data, field): |
| """Retrieve a field from a nested dict. |
| |
| Returns the first found value if it exists. Otherwise return None. |
| |
| For example, given the following data: |
| |
| data = { |
| "list": [ |
| {"foo": "bar"} |
| ] |
| } |
| |
| `retrieve_nested_field(data, "foo")` will return "bar". |
| |
| Args: |
| json_data (Any): The JSON value (list, dict, etc.) to read from. |
| field (str): The name of the field. |
| """ |
| if isinstance(json_data, dict): |
| if field in json_data: |
| return json_data[field] |
| for val in json_data.values(): |
| return_val = self.retrieve_nested_field(val, field) |
| if return_val is not None: |
| return return_val |
| if isinstance(json_data, list): |
| for item in json_data: |
| return_val = self.retrieve_nested_field(item, field) |
| if return_val is not None: |
| return return_val |
| return None |