将复杂的JSON解析为goweb REST Create()函数

huangapple go评论89阅读模式
英文:

Parsing complex JSON into goweb REST Create() function

问题

我为这个非常长的问题提前道歉。希望你能耐心等待。

我正在使用goweb库,并尝试使用示例web应用进行实验。

我一直在尝试修改RESTful示例代码,其中将Thing定义为:

type Thing struct {
    Id   string
    Text string
}

通过向http://localhost:9090/things发送适当的JSON主体的HTTP Post请求来创建Thing。这在示例代码中在Create函数中处理,具体是以下几行:

dataMap := data.(map[string]interface{})

thing := new(Thing)
thing.Id = dataMap["Id"].(string)
thing.Text = dataMap["Text"].(string)

这一切都很好,我可以运行示例服务器(监听http://localhost:9090/),服务器按预期运行。

例如:

curl -X POST -H "Content-Type: application/json" -d '{"Id":"TestId","Text":"TestText"}' http://localhost:9090/things

没有错误返回,然后我使用以下命令GETThing

curl http://localhost:9090/things/TestId

它返回:

{"d":{"Id":"TestId","Text":"TestText"},"s":200}

到目前为止,一切都很好。

现在,我想修改Thing类型,并添加一个自定义的ThingText类型,如下所示:

type ThingText struct {
    Title   string
    Body    string
}

type Thing struct {
    Id   string
    Text ThingText
}

这本身不是问题,我可以像这样修改Create函数:

thing := new(Thing)
thing.Id = dataMap["Id"].(string)
thing.Text.Title = dataMap["Title"].(string)
thing.Text.Body = dataMap["Body"].(string)

然后使用先前的curl POST请求和设置为以下JSON的请求运行:

{"Id":"TestId","Title":"TestTitle","Title":"TestBody"}

没有错误返回。

我再次可以GETThing URL,它返回:

{"d":{"Id":"TestId","Text":{"Title":"TestTitle","Body":"TestBody"}},"s":200}

同样,到目前为止,一切都很好。

现在,我的问题是:

我如何修改Create函数以允许我向其POST复杂的JSON

例如,上面返回的最后一个JSON字符串包括{"Id":"TestId","Text":{"Title":"TestTitle","Body":"TestBody"}}我想能够向端点POST*该确切的JSON*并创建Thing

我已经追踪了代码,似乎data变量的类型是来自https://github.com/stretchr/goweb/context的Context.RequestData(),而内部的Map似乎是来自https://github.com/stretchr/stew/的Object.Map类型,描述为“具有附加有用功能的map[string]interface{}。”(https://github.com/stretchr/stew/blob/master/objects/map.go#L12),特别是我注意到“支持点语法设置深层值。”(https://github.com/stretchr/stew/blob/master/objects/map.go#L112)

我无法弄清楚如何设置thing.Text.Title = dataMap...语句,以便正确解析JSON字段。我似乎只能在dataMap中使用string类型,如果我尝试使用该JSON,它会给出类似以下的错误:

 http: panic serving 127.0.0.1:59113: interface conversion: interface is nil, not string

再次,对于这个非常长的问题我表示抱歉。非常感谢你的阅读和提供的任何帮助。谢谢!

英文:

I apologise in advance for a very long question. Hopefully you'll bear with me.

I'm working with the goweb library, and experimenting with the example web app.

I've been trying to modify the RESTful example code, which defines a Thing as:

type Thing struct {
    Id   string
    Text string
}

A Thing is created by sending an HTTP Post request, with an appropriate JSON body, to http://localhost:9090/things. This is handled in the example code in the Create function, specifically the lines:

dataMap := data.(map[string]interface{})

thing := new(Thing)
thing.Id = dataMap["Id"].(string)
thing.Text = dataMap["Text"].(string)

This is all well and good, and I can run the example server (which listens on http://localhost:9090/) and the server operates as expected.

For example:

curl -X POST -H "Content-Type: application/json" -d '{"Id":"TestId","Text":"TestText"}' http://localhost:9090/things

returns with no error, and then I GET that Thing with

curl http://localhost:9090/things/TestId

and it returns

{"d":{"Id":"TestId","Text":"TestText"},"s":200}

So far, so good.

Now, I would like to modify the Thing type, and add a custom ThingText type, like so:

type ThingText struct {
    Title   string
    Body    string
}

type Thing struct {
    Id   string
    Text ThingText
}

This in itself isn't an issue, and I can modify the Create function like so:

thing := new(Thing)
thing.Id = dataMap["Id"].(string)
thing.Text.Title = dataMap["Title"].(string)
thing.Text.Body = dataMap["Body"].(string)

and the run the previous curl POST request with the JSON set to:

{"Id":"TestId","Title":"TestTitle","Title":"TestBody"}

and it returns with no error.

Once again I can GET the Thing URL and it returns:

{"d":{"Id":"TestId","Text":{"Title":"TestTitle","Body":"TestBody"}},"s":200}

Again, so far, so good.

Now, my question:

<b>how do I modify the Create function to allow me to POST complex JSON to it?</b>

For example, that last returned JSON string above includes {&quot;Id&quot;:&quot;TestId&quot;,&quot;Text&quot;:{&quot;Title&quot;:&quot;TestTitle&quot;,&quot;Body&quot;:&quot;TestBody&quot;}}. <b>I'd like to be able to POST that exact JSON to the endpoint and have the Thing created.</b>

I've followed the code back, and it seems that the data variable is of type Context.RequestData() from https://github.com/stretchr/goweb/context, and the internal Map seems to be of type Object.Map from https://github.com/stretchr/stew/, described as "a map[string]interface{} with additional helpful functionality." in particular, I noticed "Supports dot syntax to set deep values."

I can't work out how I can set up the thing.Text.Title = dataMap... statement so that the correct JSON field is parsed into it. I can't seem to use anything other than string types in the dataMap, and if I try that JSON it gives an error similar to:

 http: panic serving 127.0.0.1:59113: interface conversion: interface is nil, not string

Once again, sorry for the ridiculously long question. I really appreciate you reading, and any help you may have to offer. Thanks!

答案1

得分: 3

根据JSON包文档JSON和Go介绍所描述的,JSON数据可以通过interface{} / string映射或直接解组到结构体类型中进行通用解析。

你提供的示例代码似乎使用了通用的string-map方法,dataMap := data.(map[string]interface{})

由于你所需的JSON数据是一个对象中的对象,所以它只是一个嵌套的映射。

因此,你可以这样做:

dataMap := data.(map[string]interface{})
subthingMap := dataMap["Text"].(map[string]interface{})
thing.Text.Title = subthingMap["Title"].(string)
thing.Text.Body = subthingMap["Body"].(string)

我不确定为什么该代码使用了类型转换和通用类型,而不是直接从JSON到结构体类型进行类型安全的解组(我猜是为了抽象)。使用json包的解组到结构体类型的方法如下:

type ThingText struct {
    Title   string
    Body    string
}

type Thing struct {
    Id   string
    Text ThingText
}

…
decoder := json.NewDecoder(body)
var thingobj Thing
for {
    if err := decoder.Decode(&thingobj); err == io.EOF {
        break
    } else if err != nil {
        log.Fatal(err)
    }
    fmt.Println(thingobj)
}

其中body是一个io.Reader - 在简单/大多数情况下来自http.Response.Body

英文:

As the JSON package documentation and JSON and Go introduction describe, JSON data can be parsed either generically through interface{} / string maps or unmarshalling directly to the struct types.

The example code you linked to and you based your changes on seems to use the generic string-map approach, dataMap := data.(map[string]interface{}).

As your desired JSON data is an object in an object, it’s simply a map within a map.

So you should be able to

dataMap := data.(map[string]interface{})
subthingMap := dataMap[&quot;Text&quot;].(map[string]interface{})
thing.Text.Title = subthingMap[&quot;Title&quot;].(string)
thing.Text.Body = subthingMap[&quot;Body&quot;].(string)

I’m not sure why that code uses casts and generic types over type-safe unmarshalling directly from JSON to struct types (abstraction I guess). Using the json packages unmarshalling to struct types would go something like

type ThingText struct {
    Title   string
    Body    string
}

type Thing struct {
    Id   string
    Text ThingText
}

…
decoder := json.NewDecoder(body)
var thingobj Thing
for {
    if err := decoder.Decode(&amp;thingobj); err == io.EOF {
        break
    } else if err != nil {
        log.Fatal(err)
    }
    fmt.Println(thingobj)
}

where body is a io.Reader - in simple/most cases from http.Response.Body.

huangapple
  • 本文由 发表于 2013年6月23日 00:34:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/17252915.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定