英文:
Parsing complex JSON into goweb REST Create() function
问题
我为这个非常长的问题提前道歉。希望你能耐心等待。
我一直在尝试修改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
没有错误返回,然后我使用以下命令GET
该Thing
:
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"}
没有错误返回。
我再次可以GET
该Thing
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 {"Id":"TestId","Text":{"Title":"TestTitle","Body":"TestBody"}}
. <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["Text"].(map[string]interface{})
thing.Text.Title = subthingMap["Title"].(string)
thing.Text.Body = subthingMap["Body"].(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(&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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论