在 Hack 严格模式下反序列化 JSON
Deserialize JSON in Hack strict mode
我有一个嵌套的 JSON 文件,其中包含仅为字符串的键和值。但是JSON文件的结构并不固定,所以有时可以嵌套3层,有时只能嵌套2层。
我想知道如何在严格模式下序列化它?
"live" : {
"host" : "localhost",
"somevalue" : "nothing",
"anobject" : {
"one" : "two",
"three" : "four",
"five" : {
"six" : "seven"
}
}
}
如果我知道JSON的结构,我会自己写class,但由于键不固定,而且嵌套可以分成几层,我真的很想知道我是如何将这样一个对象切割成特定类型的。
感谢任何帮助或提示
我认为 invariant
s 在这里会很好地为您服务。首先,了解您可以在 Hack 中严格键入键控树可能会有所帮助:
<?hh // strict
class KeyedTree<+Tk as arraykey, +T> {
public function __construct(
private Map<Tk, KeyedTree<Tk, T>> $descendants = Map{},
private ?T $v = null
) {}
}
(必须是 class 因为 cyclic shape definitions are sadly not allowed)
我还没有尝试过,但是 type_structure
s 和 Fred Emmott's TypeAssert
看起来也很有趣。如果已知 JSON blob 的某些部分是固定的,那么您可以隔离嵌套的、不确定的部分,并用 invariant
s 从中构建一棵树。在整个 blob 未知的极限情况下,您可以删除 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);
}
}
以后,您仍然需要在 KeyedTree
上补充 containsKey
不变量,但这就是 Hack 中非结构化数据的现实。
我有一个嵌套的 JSON 文件,其中包含仅为字符串的键和值。但是JSON文件的结构并不固定,所以有时可以嵌套3层,有时只能嵌套2层。 我想知道如何在严格模式下序列化它?
"live" : {
"host" : "localhost",
"somevalue" : "nothing",
"anobject" : {
"one" : "two",
"three" : "four",
"five" : {
"six" : "seven"
}
}
}
如果我知道JSON的结构,我会自己写class,但由于键不固定,而且嵌套可以分成几层,我真的很想知道我是如何将这样一个对象切割成特定类型的。
感谢任何帮助或提示
我认为 invariant
s 在这里会很好地为您服务。首先,了解您可以在 Hack 中严格键入键控树可能会有所帮助:
<?hh // strict
class KeyedTree<+Tk as arraykey, +T> {
public function __construct(
private Map<Tk, KeyedTree<Tk, T>> $descendants = Map{},
private ?T $v = null
) {}
}
(必须是 class 因为 cyclic shape definitions are sadly not allowed)
我还没有尝试过,但是 type_structure
s 和 Fred Emmott's TypeAssert
看起来也很有趣。如果已知 JSON blob 的某些部分是固定的,那么您可以隔离嵌套的、不确定的部分,并用 invariant
s 从中构建一棵树。在整个 blob 未知的极限情况下,您可以删除 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);
}
}
以后,您仍然需要在 KeyedTree
上补充 containsKey
不变量,但这就是 Hack 中非结构化数据的现实。