【问题标题】:Validating JSON input with a JSON schema使用 JSON 模式验证 JSON 输入
【发布时间】:2015-07-18 05:44:41
【问题描述】:

如果您在输入文件中指定配置为架构中要求的元素,则验证 OK。 如果你追加“maxItems”:1,它不会在意你是否在输入文件中添加另一个元素,验证器仍然将其视为有效的输入文件。

即: 架构:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
        "Books": {
            "type": "object",
            "minItems": 1,
            "properties": {
                "Book": {
                    "type": "object",
                    "minItems": 1,
                    "maxItems": 1,
                    "properties": {
                        "Author": {
                            "type": "string",
                            "minItems": 1,
                            "maxItems": 1
                        }
                    },
                    "required": ["Author"]
                }
            },
            "required": ["Book"]
        }
    },
    "required": ["Books"]
}

输入文件:

{
    "Books": {
        "Book": {
            "Author": "Andreas",
            "Author": "Geir"
        }
    }
}

这不应该是一个无效的输入文件吗?

验证者:

【问题讨论】:

    标签: json validation schema jsonschema json-schema-validator


    【解决方案1】:

    根据您定义的架构,给定的 JSON 是正确的。 您的架构所说的是,对于每个对象 Author,应该有至少 1 个和最多 1 个字符串属性,您的 JSON 符合这些属性。
    除此之外,minItemsmaxItems 属性专门用于 数组,但在您的定义中,它们位于 对象。在底部的链接文档中阅读更多相关信息。

    令人困惑的部分是您期望数组是对象,而对象是数组,这有时很难区分。


    简单来说:
    JSON 对象key:value 对的set。就好像您正在定义一个对象(类)并在 OOP 语言中设置它的属性值。
    JSON 对象的基本定义

    {
      "type": "object",
      "properties": {
        "MyString": {
          "type": "string"
        },
        "MyInterger": {
          "type": "integer"
        }
    }
    

    JSON 数组 是相同(有时相似)对象或单个值的集合
    JSON 数组的基本定义

    {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
    

    还有什么有助于定义何时使用什么,是考虑您想要创建的内容,但将其视为 OOP 语言中的一个对象。

    示例:

    对于以下Book JSON 对象,我可以想象一个如图所示的类结构,然后从中创建架构:

    JSON:

    {
      "Author": "First Author",
      "TotalPages": 128,
      "Chapters": [
        {
          "Number": 1,
          "Heading": "Chapter One"
        },
        {
          "Number": 2,
          "Heading": "Chapter Two"
        }
      ]
    }
    

    我们拥有的是

    • 两个基本对象 Author (string)TotalPages (integer)
    • Chapters 对象的数组,其中包含两个基本对象Number (integer)Heading (string)

    类表示:

    public class Book
    {
      public string Author { get; set; }
      public int TotalPages { get; set; }
      // Note the array
      public Chapter[] Chapters { get; set; } // Could be List<Chapter>
    }
    
    public class Chapter
    {
      public int Number { get; set; }
      public string Heading { get; set; }
    }
    

    生成的架构:

    {
      "$schema": "http://json-schema.org/draft-04/schema#",
      "type": "object",
      "properties": {
        "Author": {
          "type": "string"
        },
        "TotalPages": {
          "type": "integer"
        },
        "Chapters": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "properties": {
              "Number": {
                "type": "integer"
              },
              "Heading": {
                "type": "string"
              }
            }
          }
        }
      },
      "required": ["Author", "Chapters"]
    }
    


    现在你会注意到我故意省略了`Books`部分,因为那是`Book`的*一个数组/集合*。如果我们想将它添加到 JSON 模式和类中,我们需要这样定义它。在此过程中,让我们还为每本书添加一个字符串数组用于“关键字”,以便清楚如何定义每个。

    首先让我们更改我们想要的输出(JSON)。
    我们希望我们的基础对象现在是Books,它应该是Book对象的集合,所以我们将Book 对象包含在[ ]中并为它添加另一本书。我们还将Keywords 的集合添加到对象Book

    {
      "Books":
      [
        {
          "Author": "First Author",
          "TotalPages": 128,
          "Chapters": [
            {
              "Number": 1,
              "Heading": "Chapter One"
            },
            {
              "Number": 2,
              "Heading": "Chapter Two"
            }
          ],
          "Keywords": [
            "This",
            "is",
            "book",
            "Alpha"
          ]
        },
        {
          "Author": "Second Author",
          "TotalPages": 256,
          "Chapters": [
            {
              "Number": 1,
              "Heading": "Erstes Kapitel"
            },
            {
              "Number": 2,
              "Heading": "Zweites Kapitel"
            }
          ],
          "Keywords": [
            "This",
            "is just",
            "Beta"
          ]
        }
      ]
    }
    

    现在我们有以下内容:

    • 一个对象Books,其中包含我们之前定义的Book 对象的数组。 (请注意,Book 从未命名,因为这会在 JSON 中添加另一个层次结构)
    • 除了我们之前定义的对象之外,我们还有一个 一个数组,由 string 组成,代表 Keywords

    让我们更改 JSON 的 class/object 表示,这样做将有助于了解如何修改架构。

    public class MyBookCollection
    {
      // Note the array!!
      public Book[] Books { get; set; } // Could also be List<Book>
    }
    
    public class Book
    {
      public string Author { get; set; }
      public int TotalPages { get; set; }
      // Note the arrays!!
      public Chapter[] Chapters { get; set; } // Could also be List<Chapter>
      public string[] Keywords { get; set; }  // Could also be List<string>
    }
    
    public class Chapter
    {
      public int Number { get; set; }
      public string Heading { get; set; }
    }
    

    我们现在知道当我们最终解析 JSON 时,我们的 dataclass 会是什么样子。让我们更改 JSON 架构,以便我们可以在验证器中使用一些东西。

    {
      "$schema": "http://json-schema.org/draft-04/schema#",
      "type": "object",
      "properties": {
        "Books": {
          "type": "array",
          "minItems": 1,
          "maxItems": 15,
          "title": "Book",
          "items": {
            "type": "object",
            "properties": {
              "Author": {
                "type": "string"
              },
              "TotalPages": {
                "type": "integer"
              },
              "Chapters": {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "object",
                  "properties": {
                    "Number": {
                      "type": "integer"
                    },
                    "Heading": {
                      "type": "string"
                    }
                  }
                }
              },
              "Keywords": {
                "type": "array",
                "minItems":2,
                "items": {
                  "type": "string"
                }
              }
            },
            "required": ["Author", "Chapters"]
          }
        }
      }
    }
    

    我在数组定义中添加了一些minItemsmaxItems,这样您就可以看到在哪里以及如何设置它们。您可以将架构和数据复制到任何验证器,并使用它们来查看它们是如何工作的。


    另外一件重要的事情:
    您无法通过模式验证来防止或检查对象内的重复属性。
    例如,使用我们的简单 JSON 对象并添加重复属性,

    {
      "Author": "First Author",
      "Author": "!!Duplicate Author!!",
      "TotalPages": 128,
      "Chapters": [
        {
          "Number": 1,
          "Heading": "Chapter One"
        },
        {
          "Number": 2,
          "Heading": "Chapter Two"
        }
      ]
    }
    

    将该 JSON 转储到提到的任何验证器中,它们都将验证为**正确**和**通过**。我检查并在 [JSON Schema Google Group][6] 上确认了这一点,目前无法通过模式定义对其进行检查。

    如何处理重复属性也是特定于库的。
    例如,C# 的 Newtonsoft.JsonServiceStack 库都将使用属性的最后一次出现。
    因此,从我们的示例中,Book.Author 属性在使用任一库反序列化后的值将是“!!Duplicate Author!!”。

    一些来源:

    【讨论】:

    • 一些非常好的例子,并在那里解释 Bernd!澄清了我很多的理解!另一个问题:如何避免用户在同一个索引中输入“作者”的两个元素?我指的是你的例子
    • @Mauster,经过一番研究,目前看来这是不可能的。如何处理具有相同名称的其他元素取决于库/实现。看看他们解释的JSON Schema Google Group
    • 我确实尝试过以各种形式的 maxProperties、maxItems、maximum、required、引用声明等强制进行验证。但由于该属性与第一个属性命名相同,因此所有验证器都通过而没有错误。
    • 哇,好详细的答案。我想我会说:“我认为当您指的是数组时,您使用的是对象”:)
    猜你喜欢
    • 2016-09-30
    • 1970-01-01
    • 2019-12-14
    • 2022-01-02
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    相关资源
    最近更新 更多