草庐IT

json - 如何解析包装的json对象

coder 2023-06-30 原文

某些 API 端点可能返回成功结果或错误,如下所示:

// ok
{
    "status": "ok",
    "payload": {
        "id": 10,
        "title": "Sample"
    },
    "request_id": "lx-VHr4OLm"
}
// error
{
    "status": "error",
    "payload": {
        "message": "internal error"
    },
    "trace_id": "lx-VHr4OLm"
}

我正在尝试寻找一种优雅的方式来使用 Go 进行解析,就像这样

.... some code
if status == "ok" {
    struct := AppStruct{} // AppStruct contains 2 fields: id and title
    _ := json.Unmarshall(payloadBody, &struct)
    return struct 
} else {
    errorStruct := ErrorStruct{} // contains 1 field for message.
    _ := json.Unmarshall(payloadBody, &errorStruct) 
    return nil, errors.New(errorStruct.Message)
}

我当前的代码不适用于成功负载:

var result map[string]interface{}
jsonErr := json.Unmarshal(body, &result)
if jsonErr != nil {
    return nil, jsonErr
}
if result["status"] == "error" {
    errorPayload := result["payload"].(map[string]string)
    return nil, errors.New(errorPayload["message"])
} else if result["status"] == "ok" {
    apiResponse := AppInfo{}
    jsonErr := json.Unmarshal([]byte(result["payload"].(string)), &apiResponse)
    if jsonErr != nil {
        return nil, jsonErr
    }
    return &apiResponse, nil
}

我在行 json.Unmarshal([]byte(result["payload"].(string)), &apiResponse)

上遇到了运行时错误

http: panic serving [::1]:51091: interface conversion: interface {} is map[string]interface {}, not string

当然,我可能有 2 个结构:成功响应和错误响应,但我认为解决我的问题的方法太复杂了。

如何以优雅的方式解析这个 JSON?

最佳答案

我真的不确定是什么问题。标准 encoding/json 不要求结构匹配 JSON 数据中的所有 字段。使用一个简单的类型来处理这个问题非常容易:

type Payload struct {
    ID      int    `json:"id"`
    Title   string `json:"title"`
    Message string `json:"message"`
}

type Response struct {
    Status  string  `json:"status"`
    ID      string  `json:"request_id"`
    TraceID string  `json:"trace_id"`
    Payload Payload `json:"payload"`
}

然后在 Response 结构中解码响应:

var resp Response
if err := json.Unmarshal(body, &resp); err != nil {
    return err
}

然后您可以简单地检查Status 字段,并确定下一步要做什么。例如:

if resp.Status == "error" {
    return fmt.Errorf("invalid response: %s - %s", resp.TraceID, resp.Payload.Message)
}
// handle resp.Payload.ID and resp.Payload.Title fields
return nil

您可以根据复杂性和您的特定需求,将状态字段的这些检查移动到响应对象上的接收函数。

也许值得为那些未在正常响应中设置的字段使用指针字段,并使用 omitempty 选项标记它们:

type Payload struct {
    ID      int     `json:"id"`
    Title   string  `json:"title"`
    Message *string `json:"message,omitempty"`
}
type Response struct {
    Status  string  `json:"status"`
    ID      string  `json:"request_id"`
    TraceID *string `json:"trace_id,omitempty"`
    Payload Payload `json:"payload"`
}

有了这样的类型,不再需要依赖硬编码的字符串常量来检查错误。相反,您可以像这样轻松实现更通用的检查:

func (r Response) IsError() bool {
    return (r.TraceID == nil) // will be set in case of an error response
}

更新

正如您在评论中指出的那样,响应主体实际上比示例中的 2 个字段大得多。当然,复制粘贴结构定义,或编写映射函数以将 Payload 映射到您已有的类型上是没有意义的。

这里的答案是:组合。

type Payload struct {
    AppStruct // embedded the AppStruct type
    Message   *string `json:"message"`
}

Response 类型保持原样。如果响应成功,您可以直接从响应中获取 AppStruct,如下所示:

appStruct := resp.Payload.AppStruct

这是有效的,因为类型是嵌入的。请注意,那里没有任何 json 标记。至少就解码而言,嵌入式结构是 Payload 结构的一部分。因此,该类型的所有导出字段将直接解码到结构中。

关于json - 如何解析包装的json对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54025961/

有关json - 如何解析包装的json对象的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  10. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

随机推荐