使用未知键序列化 JSON

Posted

技术标签:

【中文标题】使用未知键序列化 JSON【英文标题】:Serialization of JSON with unknown key 【发布时间】:2019-01-06 01:54:36 【问题描述】:

我正在使用json_serializable pub package 来满足我所有的 RESTful API 需求,而且效果很好,但现在我正在尝试序列化来自 Wikipedia 的内容。

我的问题是响应结构,其中一个键是预先未知的,并且根据返回的页面 ID 是动态的,这是一个示例:

我只想获取页面的介绍部分,我的查询是: https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&redirects=1&exintro=1&explaintext=1&titles=Stack%20Overflow

响应将是:


    "batchcomplete": "",
    "query": 
        "pages": 
            "21721040": 
                "pageid": 21721040,
                "ns": 0,
                "title": "Stack Overflow",
                "extract": "Stack Overflow is a privately held website, the flagship site of ......."
            
        
    

最终我只想获得内部extract 字符串,但“在路上”我在pages 下有页面ID。

当我不知道关键是什么时,我找不到如何获取对象,既不使用json_serializable package,也不明确编写 JSON 导入代码。有可能吗?

PS,我确实认为我找到了一种需要两次 API 调用的方法 - 如果我添加 indexpageids 标志并将其设置为 true,i will receive an additional dictionary entry called pageids with the page id numbers as string,那么我可以在第二次 API 调用。

我仍然不知道我会做的确切方式,但我很确定这是可能的,但每次发送 2 个 API 请求很昂贵,我想知道是否有更优雅的方式来做.

【问题讨论】:

你不能使用Map<String, Page> 作为pages 的类型吗? 【参考方案1】:

确保您使用最新版本的 json_serializable。对键值对使用Map<String, Page>

@JsonSerializable()
class Query 
  final Map<String, Page> pages;

  Query(this.pages);
  factory Query.fromJson(Map<String, dynamic> json) => _$QueryFromJson(json);
  Map<String, dynamic> toJson() => _$QueryToJson(this);


只是为了证明它有效,下面是 json_serializable 生成的内容:

Query _$QueryFromJson(Map<String, dynamic> json) 
  return new Query((json['pages'] as Map<String, dynamic>)?.map((k, e) =>
      new MapEntry(
          k, e == null ? null : new Page.fromJson(e as Map<String, dynamic>))));

【讨论】:

【参考方案2】:

您可以获取 json 的键,然后使用该键获取值作为

var response = await http.get(API_ENDPOINT);
var responseJson = json.decode(response.body);
Map<String, dynamic> json =responseJson['query']['pages'];
String pageId = json.keys.toList()[0]; // 0 for first page, you can iterate for multiple pages
firstPage = json[pageId]; 
String title = firstPage["title"];

【讨论】:

【参考方案3】:

如果动态键中还有另一层动态键,你会怎么做?我必须为我的第二个类编辑生成的 json_serializable 以仅使用 json 而不是生成的 json['key']。

Page _$PageFromJson(Map<String, dynamic> json) 
  return new Page((json as Map<String, dynamic>)?.map((k, e) =>
  new MapEntry(
      k, e == null ? null : new SecondPage.fromJson(e as Map<String, dynamic>))));

感觉有更好的办法?因为每次我对任何文件进行更改并运行生成器时,生成的代码都会被覆盖。

我是 SO 新手,所以我无法评价答案和编写 cmets。但是 boformer 的回答对我有所帮助,但后来我遇到了第二个问题。

编辑:我通过反复试验找到了解决方案,使用:

class Query 
  final Map<String, Map<String, SecondPage>> pages;

【讨论】:

如果您在这篇文章或其他任何地方都找不到您的问题的答案,这是一个提出自己问题的好机会。作为答案发布的问题往往不会被能够回答的人看到。 是的,我想过,但我不想只做一点修改就创建一个副本。不知道我是否可以参考一个问题。但我现在找到了答案并更新了我的回复。谢谢

以上是关于使用未知键序列化 JSON的主要内容,如果未能解决你的问题,请参考以下文章

使用已知和未知字段反序列化 json

如何将多个键值条目的 JSON 对象反序列化为 Rust 中的自定义结构

来自 json 的原型反序列化将在新字段或未知枚举值上失败

当对象数量未知时反序列化Json

jackson 序列化忽略未知字段: How to Ignore Unknown Properties While Parsing JSON in Java

使用动态键反序列化 JSON