Search code examples
arraysjsonmultidimensional-arrayjqjsonschema

How can I write a jq expression to convert nested array objects in JSON to a JSON schema?


I have a JSON structure like this:

{
"objectSchema": {
    "fields": [
        {
            "name": "OBJECTID",
            "type": "esriFieldTypeOID",
            "alias": "OBJECTID",
            "domain": null,
            "editable": false,
            "nullable": false,
            "defaultValue": null,
            "modelName": "OBJECTID"
        },
        {
            "name": "ENABLED",
            "type": "esriFieldTypeSmallInteger",
            "alias": "ENABLED",
            "domain": {
                "type": "codedValue",
                "name": "EnabledDomain",
                "description": "Geometric Network Enabled domain",
                "codedValues": [
                    {
                        "name": "False",
                        "code": 0
                    },
                    {
                        "name": "True",
                        "code": 1
                    }
                ],
                "mergePolicy": "esriMPTDefaultValue",
                "splitPolicy": "esriSPTDefaultValue"
            },
            "editable": true,
            "nullable": true,
            "defaultValue": 1,
            "modelName": "ENABLED"
        },
        {
            "name": "GLOBALID",
            "type": "esriFieldTypeGlobalID",
            "alias": "GLOBALID",
            "domain": null,
            "editable": false,
            "nullable": false,
            "length": 38,
            "defaultValue": null,
            "modelName": "GLOBALID"
        }
    ]
   }
   }

My current jq -

def convertToSchema: if type == "array" then if length == 0 then {"type": "array", "items": {"type": "string"}} else {"type": "array", "items": (reduce map(convertToSchema)[] as $i ({}; . *= $i))} end elif type == "object" then {"type": "object", "properties": map_values(convertToSchema)} elif type == "boolean" then {"type": "boolean"} elif type == "number" then {"type": "number"} elif type == "string" then {"type": "string"} elif type == "null" then {"type": "string"} else {"type": (type | tostring)} end; .objectSchema | convertToSchema

My current output / converted JSON schema -

{
"type": "object",
"properties": {
"fields": {
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string"
      },
      "type": {
        "type": "string"
      },
      "alias": {
        "type": "string"
      },
      "domain": {
        "type": "string",
        "properties": {
          "type": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "codedValues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "code": {
                  "type": "number"
                }
              }
            }
          },
          "mergePolicy": {
            "type": "string"
          },
          "splitPolicy": {
            "type": "string"
          }
        }
      },
      "editable": {
        "type": "boolean"
      },
      "nullable": {
        "type": "boolean"
      },
      "defaultValue": {
        "type": "string"
      },
      "modelName": {
        "type": "string"
      },
      "length": {
        "type": "number"
      }
    }
  }
}
}
}

Here "domain" field should be of "type": "object", but while converting it to schema in output I am getting it as "type": "string".

Please give me a proper jq expression that will handle above situations in all JSON nested object cases and ensure that each item in the array is considered and merged properly before applying the convertToSchema function and create a combined schema that includes all unique properties from all items in the array.

Providing JQPlay link with proper solution will be more helpful.


Solution

  • Instead of add, you need to merge with * operator:

    jq '
    def convertToSchema:
      if type == "array" then
        if length == 0 then {"type": "array", "items": {"type": "string"}}
        else {"type": "array", "items":
              (reduce map(convertToSchema)[] as $i ({}; . *= $i))}
        end
      elif type == "object" then {
        "type": "object",
        "properties": map_values(convertToSchema)}
      elif type == "boolean" then {"type": "boolean"}
      elif type == "number" then {"type": "number"}
      elif type == "string" then {"type": "string"}
      elif type == "null" then {"type": "string"}
      else {"type": (type | tostring)} end;
    .objectSchema | convertToSchema
    ' input.json
    

    Output:

    {
      "type": "object",
      "properties": {
        "fields": {
          "type": "object",
          "properties": {
            "fieldArray": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "a": {
                    "type": "string"
                  },
                  "c": {
                    "type": "string"
                  },
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    }