【问题标题】:How can I de-duplicate two similar JSON Schemas?如何对两个相似的 JSON 模式进行重复数据删除?
【发布时间】:2020-07-21 23:03:59
【问题描述】:

我在架构定义中有以下服务对象:

"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、mem 和其他选项定义。每个服务都可以有一个覆盖对象,可用于替换每个环境的服务对象属性...例如

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

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

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

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

为简洁起见,完整架构减去 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"
    }
  }
}

【问题讨论】:

  • 请提供您的完整架构,包括您使用的草稿。然后我也许可以提供帮助 =]
  • 没有注意到我遗漏了草稿!哈哈我正在使用draft-07。我可以使用完整的架构进行更新。它应该包括 allOf 块吗?它变得非常不守规矩
  • 尽量只包含与问题相关的内容。在这种情况下,我认为这一切都可能是相关的。
  • 在阅读您的评论之前我正在编辑。我翻阅了 allOf 块,它们是相同的。就相关性而言,假设 allOf 块也可以进行重复数据删除是否安全?
  • 是的。感谢更新

标签: jsonschema json-schema-validator


【解决方案1】:

由于您为某些不同的属性添加了字段,因此您无法对所有属性进行重复数据删除。 errorMessage 不是 JSON Schema 规范的一部分,因此它的使用仅限于您正在使用的库。

您可以删除一些属性,并且您已经使用了引用 ($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 对象的值是子模式。

【讨论】:

  • 是的,我一直在使用 ajv-errors 库来提供特定的错误信息。考虑到我使用的重叠属性,错误结果非常模糊。如果它们的错误消息相同,是否可以引用属性子模式?我的主要印象是,考虑到我正在使用的通配符模式属性/键,我会受到限制。
  • 从我现在收集到的信息来看,任何重复的子模式(在这种情况下包括覆盖/服务的属性)都可以作为定义进行维护,然后在服务和服务的定义中引用覆盖。尽管考虑到它们的一些差异(键和错误消息),我仍然需要将覆盖和服务定义分开吗?
  • 在回答您的第一条评论时,是的。任何相同的子模式都可以去重和引用;您对正则表达式的使用不会影响该方面。第二条评论:对。如果差异是例如某些字段是必需的,那么除了基本引用之外,还可以应用该方面,例如扩展一个类,如果您愿意的话。
  • 感谢您接受我的回答。如果你愿意支持我支持社区,请随时给我买杯咖啡:buymeacoffee.com/relequestual
猜你喜欢
  • 2020-04-01
  • 2019-09-06
  • 2021-01-15
  • 2020-05-26
  • 1970-01-01
  • 2019-07-29
  • 2021-10-15
  • 2014-01-27
  • 1970-01-01
相关资源
最近更新 更多