Проверка ввода JSON с помощью схемы JSON

Если вы укажете элементы во входном файле, которые настроены как требования в схеме, это подтверждается. И если вы добавите «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"
        }
    }
}

Разве это не должен быть недопустимый входной файл?

Валидаторы:


person Andreas Baaserud    schedule 07.05.2015    source источник


Ответы (1)


В соответствии с вашей определенной схемой данный JSON является правильным. Ваша схема говорит о том, что для каждого объекта Author должно быть минимум 1 и максимум 1 строковое свойство, которому соответствует ваш JSON.
Кроме того, свойства minItems и maxItems предназначены специально для массивов, но в вашем определении они относятся к объектам. Подробнее об этом читайте в связанной документации внизу.

Часть, где возникает путаница, заключается в том, что вы ожидаете, что массивы будут объектами, а объекты будут массивами, которые иногда бывает трудно различить.


Проще говоря:
объект JSON – это набор пар ключ:значение. Это было бы так, как если бы вы определяли объект (класс) и задавали значения его свойств на языке ООП.
Базовое определение объекта JSON:

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

Массив JSON – это набор одинаковых, иногда похожих объектов или отдельных значений.
Основное определение массива JSON:

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

Что также может помочь определить, что и когда использовать, — это думать о том, что вы хотите создать, но как об объекте на языке ООП.

Пример:

Для следующего 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, нам нужно будет определить это как таковое. Пока мы на этом, давайте также добавим массив строк для каждой книги для Keywords, чтобы было ясно, как можно определить каждую.

Во-первых, давайте изменим желаемый результат (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, это поможет узнать, как изменить схему.

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. Давайте изменим схему 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"]
      }
    }
  }
}

Я добавил несколько minItems и maxItems для определения массивов, чтобы вы могли видеть, где и как их установить. Вы можете скопировать схему и данные в любой из валидаторов и поэкспериментировать с ними, чтобы увидеть, как они работают.


Еще один важный момент:
Вы не можете предотвратить дублирование свойств внутри объекта или проверить его с помощью проверки схемы.
В качестве примера можно использовать наш простой объект JSON и добавление повторяющегося свойства. ,

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


отправив этот JSON в любой из упомянутых валидаторов, все они будут проверены как правильные и пройденные. Я проверил, и это подтверждено в группе Google схемы JSON. что его нельзя проверить в настоящее время с помощью определения схемы.

Как обрабатываются повторяющиеся свойства, также зависит от библиотеки.
Например, как Newtonsoft Библиотеки .Json и ServiceStack для C# будут использовать последнее вхождение свойства.
Итак, from В нашем примере значение свойства Book.Author после десериализации с любой библиотекой будет "!!Дубликат автора!!".

Некоторые источники:

person Bernd Linde    schedule 07.05.2015
comment
Несколько очень хороших примеров и объяснения Бернд! многое прояснилось в моем понимании! Другой вопрос: как сделать так, чтобы пользователь не вводил два элемента Author в один и тот же индекс? Я имею в виду ваши примеры - person Andreas Baaserud; 08.05.2015
comment
@Mauster, после некоторых исследований кажется, что в настоящее время это невозможно. То, как обрабатываются дополнительные элементы с тем же именем, зависит от библиотеки/реализации. Посмотрите группу Google по схемам JSON, где они объясняют Это. - person Bernd Linde; 08.05.2015
comment
Я пробовал форсировать валидацию в различных формах maxProperties, maxItems, максимальных, обязательных, ссылочных деклараций и т. д. Но поскольку свойство названо так же, как и первое, все валидаторы проходят без ошибок. - person Bernd Linde; 08.05.2015
comment
Ого, какой подробный ответ. Думаю, я бы просто сказал: я думаю, вы используете объект, когда имеете в виду массив :) - person Steve Bennett; 25.05.2015