【问题标题】:Use conditional statements on json schema based on another schema object基于另一个模式对象对 json 模式使用条件语句
【发布时间】:2019-08-09 01:09:49
【问题描述】:

我有一个像这样的 json 对象:

{
  "session": {
    "session_id": "A",
    "start_timestamp": 1535619633301
  },
  "sdk": {
    "name": "android",
    "version": "21"
  }
}

sdk name 可以是 android or ios。而session_id 是基于sdk json 中的name field。我使用条件语句(使用草案 7)编写了json schema,如下所示:

但它以一种意想不到的方式工作:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/Base",
  "definitions": {
    "Base": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "session": {
          "$ref": "#/definitions/Session"
        },
        "sdk": {
          "$ref": "#/definitions/SDK"
        }
      },
      "title": "Base"
    },
    "Session": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "start_timestamp": {
          "type": "integer",
          "minimum": 0
        },
        "session_id": {
          "type": "string",
          "if": {
            "SDK": {
              "properties": {
                "name": {
                  "enum": "ios"
                }
              }
            }
          },
          "then": {
            "pattern": "A"
          },
          "else": {
            "pattern": "B"
          }
        }
      },
      "required": [
        "session_id",
        "start_timestamp"
      ],
      "title": "Session"
    },
    "SDK": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "version": {
          "type": "string"
        },
        "name": {
          "type": "string",
          "enum": [
            "ios",
            "android"
          ]
        }
      },
      "required": [
        "name",
        "version"
      ],
      "title": "SDK"
    }
  }
}

所以下面的 JSON 通过了:

{
      "session": {
        "session_id": "A",
        "start_timestamp": 1535619633301
      },
      "sdk": {
        "name": "ios",
        "version": "21"
      }
    }

但这失败了:

{
      "session": {
        "session_id": "B",
        "start_timestamp": 1535619633301
      },
      "sdk": {
        "name": "android",
        "version": "21"
      }
    }

有人可以解释一下吗?..即使这样也通过了:

{
      "session": {
        "session_id": "A",
        "start_timestamp": 1535619633301
      },
      "sdk": {
        "name": "android",
        "version": "21"
      }
    }

【问题讨论】:

  • JSON Schema中的if关键字,值必须是JSON Schema。目前,如果您要在架构中获取 if 的值,并将其用作 JSON 架构,您期望会发生什么?你看到问题了吗? =](如果没有,这里是为了进一步提供帮助,但我觉得这是一个简单的误解。)
  • @Relequestual 你能详细说明一下吗?我没有看到问题
  • 当然。我已将架构复制到一个要点,以便我可以参考行号:gist.github.com/Relequestual/…
  • 我明白你在说什么。但是我从这里验证了 if 语句的语法:json-schema.org/understanding-json-schema/reference/…。我引用 SDK 名称字段的方式是否错误?

标签: json schema jsonschema json-schema-validator


【解决方案1】:

我认为您遇到了与this question 类似的问题。

@Relequestual 是正确的,因为您需要在 SDK 标注周围使用 properties 关键字。但是对于你想做的事情,你需要重新组织。

子模式仅在实例中的它们的级别上运行,而不是在根目录中。

考虑一个简单的 JSON 对象实例的这个架构,其中包含一个 one 和一个 two 属性:

{
  "properties": {
    "one": {
      "enum": ["yes", "no", "maybe"]
    },
    "two": {
      "if": {
        "properties": {
          "one": {"const": "yes"}
        }
      },
      "then": {
        ...       // do some assertions on the two property here
      },
      "else": {
        ...
      }
    }
  }
}

two属性下的if关键字只能考虑two属性下的实例部分(即two的值)。它没有查看实例的根目录,因此根本看不到 one 属性。

要使two 属性子模式下的子模式可以看到实例中的one 属性,您必须将if 移到properties 关键字之外。

{
  "if": {
    "properties": {
      "one": {"const" : "yes"}
    }
  },
  "then": {
    ...       // do some assertions on the two property here
  },
  "else": {
    ...       // assert two here, or have another if/then/else structure to test the one property some more
  }
}

对于one 的两个可能值,这非常好。即使是三个可能的值也不错。但是,随着one 的可能值增加,ifs 的嵌套也会增加,这会使您的架构难以阅读(并且可能会使验证速度变慢)。

我建议不要使用if/then/else 构造,而是使用anyOfoneOf,其中每个子模式代表实例的有效状态,给定one 的不同值.

{
  "oneOf": [
    {
      "properties": {
        "one": {"const": "yes"},
        "two": ...         // do some assertions on the two property here
      }
    },
    {
      "properties": {
        "one": {"const": "no"},
        "two": ...         // do some assertions on the two property here
      }
    },
    {
      "properties": {
        "one": {"const": "maybe"},
        "two": ...         // do some assertions on the two property here
      }
    }
  ]
}

在我看来,这更干净。

希望这种解释可以帮助您重建架构以允许其他实例通过。

【讨论】:

    【解决方案2】:

    您必须将条件提升到足够高的级别,以便能够引用它需要引用的所有属性。在这种情况下,这就是 /definitions/Base 模式。然后你只需要按照 Relequestual 的解释正确地编写你的模式。

    {
      "$ref": "#/definitions/Base",
      "definitions": {
        "Base": {
          "type": "object",
          "properties": {
            "session": { "$ref": "#/definitions/Session" },
            "sdk": { "$ref": "#/definitions/SDK" }
          },
          "allOf": [
            {
              "if": {
                "properties": {
                  "sdk": {
                    "properties": {
                      "name": { "const": "ios" }
                    }
                  }
                },
                "required": ["sdk"]
              },
              "then": {
                "properties": {
                  "session": {
                    "properties": {
                      "session_id": { "pattern": "A" }
                    }
                  }
                }
              },
              "else": {
                "properties": {
                  "session": {
                    "properties": {
                      "session_id": { "pattern": "B" }
                    }
                  }
                }
              }
            }
          ]
        },
      ...
    }
    

    【讨论】:

      【解决方案3】:

      if 的值必须是 JSON 架构。如果您采用行 https://gist.github.com/Relequestual/f225c34f6becba09a2bcaa66205f47f3#file-schema-json-L29-L35 (29-35) 并将其单独用作 JSON Schema,则不会施加验证约束,因为对象的顶层没有 JSON Schema 关键字。

      {
        "SDK": {
          "properties": {
            "name": {
              "enum": "ios"
            }
          }
        }
      }
      

      这在规范中是允许的,因为人们可能希望通过添加自己的关键字来扩展 JSON Schema 的功能。所以它是“有效的” JSON Schema,但实际上并没有做任何事情。

      您需要将 properties 添加到架构中才能有意义。

      {
        "properties": {
          "SDK": {
            "properties": {
              "name": {
                "const": "ios"
              }
            }
          }
        }
      }
      

      另外,enum 必须是一个数组。当您只有一个项目时,您可以使用const

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-29
      • 2021-05-31
      • 2016-11-21
      • 1970-01-01
      • 2015-06-29
      相关资源
      最近更新 更多