Десериализовать JSON в строгом режиме Hack

У меня есть вложенный файл JSON, состоящий из ключей и значений, которые являются только строковыми. Но структура файла JSON не является фиксированной, поэтому иногда он может быть вложен в 3 уровня, иногда только в 2 уровня. Интересно, как я мог сериализовать это в строгом режиме?

  "live" : {
"host" : "localhost",
"somevalue" : "nothing",
"anobject" : {
  "one" : "two",
  "three" : "four",
  "five" : {
    "six" : "seven"
  }
}

}

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

Любая помощь или советы приветствуются

1 ответ

Решение

Я думаю invariantЗдесь вам хорошо послужат. Прежде всего, может быть полезно знать, что вы можете вводить дерево ключей строго в Hack:

<?hh // strict
class KeyedTree<+Tk as arraykey, +T> {
  public function __construct(
    private Map<Tk, KeyedTree<Tk, T>> $descendants = Map{},
    private ?T $v = null
  ) {}
}

(Это должен быть класс, потому что определения циклических форм, к сожалению, не допускаются)

Я еще не пробовал, но type_structureи Фреда ЭммоттаTypeAssert посмотрите также, чтобы представлять интерес. Если известно, что какая-то часть вашего большого двоичного объекта JSON исправлена, вы можете выделить вложенную неопределенную часть и построить из нее дерево с помощью invariants. В предельном случае, когда весь капля неизвестна, вы можете удалить TypeAssert так как нет интересной фиксированной структуры для утверждения:

use FredEmmott\TypeAssert\TypeAssert;
class JSONParser {
    const type Blob = shape(
        'live' => shape(
            'host' => string, // fixed
            'somevalue' => string, // fixed
            'anobject' => KeyedTree<arraykey, mixed> // nested and uncertain
        )
    );
    public static function parse_json(string $json_str): this::Blob {
        $json = json_decode($json_str, true);
        invariant(!array_key_exists('anobject', $json), 'JSON is not properly formatted.');
        $json['anobject'] = self::DFS($json['anobject']);
          // replace the uncertain array with a `KeyedTree`
        return TypeAssert::matchesTypeStructure(
            type_structure(self::class, 'Blob'),
            $json
        );
        return $json;
    }
    public static function DFS(array<arraykey, mixed> $tree): KeyedTree<arraykey, mixed> {
        $descendants = Map{};
        foreach($tree as $k => $v) {
            if(is_array($v))
                $descendants[$k] = self::DFS($v);
            else
                $descendants[$k] = new KeyedTree(Map{}, $v); // leaf node
        }
        return new KeyedTree($descendants);
    }
}

В будущем вам все равно придется дополнить containsKey инварианты на KeyedTree, но это реальность с неструктурированными данными в Hack.

Другие вопросы по тегам