如何删除两个相似的 JSON 模式?

How can I de-duplicate two similar JSON Schemas?

我在模式定义中有以下服务对象:

"services": {
  "type": "object"
  "propertyNames": {
    "pattern": "^[A-Za-z_]*$",
    "errorMessage": "service names must be non-numeric"
  }
  "patternProperties": {
    "^[A-Za-z_]*$": {
      "type": "object"
      "properties": {
        "source": {
          "type": ["string", "object"]
        },
        "port": {
          "type": "number",
          "errorMessage": "invalid port value"
        },
        "cpu": {
          "type": "number",
          "errorMessage": "invalid cpu value
        },
        "memory": {
          "type": "number",
          "errorMessage": "invalid memory value
        },
        "overrides": { "$ref": "#/definitions/overrides" },
        "allOf": [...]
      }
   }
 }

在单独的定义中,我维护了以下覆盖对象:

      "overrides": {
      "type": "object",
      "propertyNames": {
        "pattern": "^(dev|int|trn|qa|stag|prod)?$",
        "errorMessage": "override stage name must be approved environment (dev, int, trn, qa, stag, prod)"
       },
      "patternProperties": {
        "^(dev|int|trn|qa|stag|prod)?$": {
          "type": "object",
          "propertyNames": {
            "pattern": "^[A-Za-z_]*$",
            "errorMessage": "override property name must be non-numeric"
           },
          "properties": {
            "$ref": "..."
          },
          "allOf": [...]
        }
      }
    }

对于某些上下文,这是一个配置文件,其中使用端口、源、cpu、内存和其他选项定义服务对象。每个服务都可以有一个覆盖对象,可用于替换每个环境的服务对象属性...例如

{
   "services": {
      "main": {
         "source": "https://github.com/foo,
         "port": 3000,
         "cpu": 256,
         "memory": 1,
         "overrides": {
            "prod": {
               "port": 8443
            }
         }
      }
   }
}

我的目标是对此进行优化,这样我就不必维护两个相同的模式。现在,服务定义中存在的每个 属性 基本上都被复制为覆盖对象的 属性。我还维护单独的“allOf”验证,因为服务对象属性是必需的,但覆盖对象属性都是可选的。

是否可以利用 $ref 功能从覆盖定义中指向服务定义的属性?此外,是否有可能完全摆脱覆盖模式并重用服务模式,或者它们的模式 patternProperties 中的差异是否阻止了这种情况?

感谢任何可能帮助我克服困难的指导。

为简洁起见,完整架构减去了 allOf 块:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "services": {
      "type": "object",
      "propertyNames": {
        "pattern": "^[A-Za-z_]*$",
        "errorMessage": "service names must be non-numeric"
      },
      "patternProperties": {
        "^[A-Za-z_]*$": {
          "type": "object",
          "propertyNames": {
            "pattern": "^[A-Za-z_]*$",
            "errorMessage": "service property names must be non-numeric"
           },
          "properties": {
            "source": {
              "type": ["string", "object"]
            },
            "port": {
              "type": "number",
              "errorMessage": "invalid port value"
            },
            "cpu": {
              "type": "number",
              "enum": [256, 512, 1024, 2048, 4096],
              "errorMessage": "invalid cpu value - should be one of the following: 256, 512, 1024, 2048, 4096"
            },
            "memory": {
              "type": "number",
              "enum": [0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
                12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                22, 23, 24, 25, 26, 27, 28, 29, 30],
              "errorMessage": "memory value should be 0.5 - 30"
            },
            "environment": {
              "type": "object",
              "errorMessage": "invalid environment variables defined  - must be an object"
            },
            "overrides": { "$ref": "#/definitions/overrides" }
          },
          "required": ["source", "port", "cpu", "memory", "min_instances"],
          "errorMessage": {
            "required": {
              "source": "missing service source",
              "port": "missing service port",
              "cpu": "missing service cpu",
              "memory": "missing service memory",
              "min_instances": "missing minimum required service instances"
            },
            "type": "service must have at least one defined property",
            "additionalProperties": "invalid property found in service definition"
          },
          "allOf": [...],
          "additionalProperties": false
        }
      },
      "errorMessage": { "type": "must have at least one defined service" }
    },
    "overrides": {
      "type": "object",
      "propertyNames": {
        "pattern": "^(dev|int|trn|qa|stag|prod)?$",
        "errorMessage": "override stage name must be approved environment (dev, int, trn, qa, stag, prod)"
       },
      "patternProperties": {
        "^(dev|int|trn|qa|stag|prod)?$": {
          "type": "object",
          "propertyNames": {
            "pattern": "^[A-Za-z_]*$",
            "errorMessage": "override property name must be non-numeric"
           },
          "properties": {
            "port": {
              "type": "number",
              "errorMessage": "invalid port value"
            },
            "cpu": {
              "type": "number",
              "enum": [256, 512, 1024, 2048, 4096],
              "errorMessage": "invalid cpu value - should be one of the following: 256, 512, 1024, 2048, 4096"
            },
            "memory": {
              "type": "number",
              "enum": [0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
                12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                22, 23, 24, 25, 26, 27, 28, 29, 30],
              "errorMessage": "memory value should be 0.5 - 30"
            },
            "environment": {
              "type": "object",
              "errorMessage": "invalid environment variables defined  - must be an object"
            }
          },
          "errorMessage": {
            "type": "environment must have at least one defined override",
            "additionalProperties": "invalid property found in override definition"
          },
          "allOf": [...],
          "additionalProperties": false
        }
      },
      "errorMessage": { "type": "overrides must have at least one defined environment" }
    }
  },
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "errorMessage": "app name should be string"
    },
    "account": {
      "type": "string",
      "errorMessage": "invalid account value"
    },
    "id": {
      "type": "string",
      "errorMessage": "invalid id tag value"
    },
    "services": {
      "$ref": "#/definitions/services"
    },
    "stages": {
      "type": "array",
      "errorMessage": "invalid stages option - must be an array"
    }
  },
  "required": ["name", "account", "id", "services"],
  "errorMessage": {
    "required": {
      "name": "missing app name",
      "account": "missing designated account",
      "id": "missing designated id tag",
      "services": "no services are defined"
    }
  }
}

因为您已经为一些不同的属性添加了字段,所以您不能删除所有的重复项。 errorMessage 不是 JSON 架构规范的一部分,因此它的使用仅限于您正在使用的库。

您可以删除一些属性,并且您已经使用了引用 ($ref)。

您可以将 memory 组件移动到它自己的定义...

...
"definitions": {
    "componentMemory": {
      "type": "number",
      "enum": [0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
        12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
        22, 23, 24, 25, 26, 27, 28, 29, 30],
      "errorMessage": "memory value should be 0.5 - 30"
    },
...

然后在两个子模式位置引用它...

...
            "cpu": {
              "type": "number",
              "enum": [256, 512, 1024, 2048, 4096],
              "errorMessage": "invalid cpu value - should be one of the following: 256, 512, 1024, 2048, 4096"
            },
            "memory": {
              "$ref": "#/definitions/services"
            },
...

如果您不太关心错误消息,则可以进一步删除重复数据,但我猜您确实关心它们。

因此,只要子模式相同,就可以删除重复项。

properties 对象的值是子模式。