英文:
Returning JSON structs of different types, dynamically?
问题
我正在玩Go,并且在将其他语言中的“继承”设计适应其结构时遇到了一些问题。我以OCaml和其他一些具有类似结构的语言进行编码,但我感到困惑。由于没有类型继承,使用共享结构从不同的位置返回JSON变得有点奇怪。
我必须根据需要递归地遍历输入数据,以构建一个要响应的单个JSON对象。
举个例子:
{
"appVersion": "1.0.0",
"messageStatus": "received"
}
和:
{
"appVersion": "1.0.0",
"uploadStatus": "received"
}
到目前为止,我在Go中找到的唯一方法是将包含"appVersion"的基本结构复制并粘贴到两个独立的输出生成函数中,但我不想这样做,因为我不想维护相同的代码两次。
以下是我试图解决这个问题的地方:
type JSONResponse struct {
appVersion string
}
type MessageJSONResponse struct {
JSONResponse
messageStatus string
}
type UploadJSONResponse struct {
JSONResponse
uploadStatus string
}
然后:
type Message struct {
formattingVersion *int
}
func NewMessageObject(r *http.Request) (bool, *MessageJSONResponse) {
message := new(Message)
if (true) {
// #TODO: INSERT LOGIC HERE!
*message.formattingVersion = 2;
}
if (message.formattingVersion != nil) {
response := new(MessageJSONResponse)
response.messageStatus = "OK";
return false, errorResponse;
}
return true, nil;
}
还有:
func init() {
http.Handle("/endpoints/message", JSONResponseHandler(messageHandler));
}
func JSONResponseHandler(h func (w http.ResponseWriter, r *http.Request) interface {}) http.Handler {
// #TODO - convert `JSONResponse` into actual JSON or output JSON Error!
}
func messageHandler(w http.ResponseWriter, r *http.Request) JSONResponse { // #BROKEN TYPES?
hasError, messageResponse := NewMessageObject(r);
if (hasError || messageResponse==nil) { return nil } // #TODO
////
// #TODO ... more message things.
return messageResponse;
}
这种方法(对于任何代码错误,我很抱歉,今天真的很长,我要去睡觉了)不起作用,因为为了传递不同的返回值...类型不能改变等等。
**JSONResponseHandler
**包装方法在我的端上实际上是有效的,但由于类型的变化,只能使用interface {}
类型,所以我将其省略了,因为它会使代码变得混乱。然而,如果我在后续的块中使用interface {}
以及带有可选星号的返回属性(例如"NewMessageObject"),JSON构造函数似乎会忽略这些值,因为它们被包装在一个空接口中,而不是以原始类型暴露。然而,它们必须有一个nil选项...
问题出在哪里?总体设计有问题吗?我基本上是想以一种整洁的抽象方式根据输入数据构建一个JSON对象响应(或以JSON格式返回错误)。
英文:
I'm playing around with Go and having trouble adapting some of my "inheriting" designs from other languages to its structure. I've coded in OCaml and some other languages bearing a similar structure, but I'm confused. Without type inheritance it becomes a bit strange to return JSON from different places using shared structures.
I have to iterate recursively through input data as needed to build a single JSON object to respond with.
Take for instance:
{
"appVersion": "1.0.0",
"messageStatus": "received"
}
... and:
{
"appVersion": "1.0.0",
"uploadStatus": "received"
}
The only way I can find to make this work in Go so far is to copy and paste the base structure that includes "appVersion" into the two output generating functions separately, but I don't want to do that because I don't want to have to maintain the same set of code twice.
Here's where I'm stuck trying to fix that:
type JSONResponse struct {
appVersion string
}
type MessageJSONResponse struct {
JSONResponse
messageStatus string
}
type UploadJSONResponse struct {
JSONResponse
uploadStatus string
}
... Then:
type Message struct {
formattingVersion *int
}
func NewMessageObject(r *http.Request) (bool, *MessageJSONResponse) {
message := new(Message)
if (true) {
// #TODO: INSERT LOGIC HERE!
*message.formattingVersion = 2;
}
if (message.formattingVersion != nil) {
response := new(MessageJSONResponse)
response.messageStatus = "OK"
return false, errorResponse
}
return true, nil
}
... And:
func init() {
http.Handle("/endpoints/message", JSONResponseHandler(messageHandler))
}
func JSONResponseHandler(h func (w http.ResponseWriter, r *http.Request) interface {}) http.Handler {
// #TODO - convert `JSONResponse` into actual JSON or output JSON Error!
}
func messageHandler(w http.ResponseWriter, r *http.Request) JSONResponse { // #BROKEN TYPES?
hasError, messageResponse := NewMessageObject(r)
if (hasError || messageResponse==nil) { return nil } // #TODO
////
// #TODO ... more message things.
return messageResponse;
};
This approach (sorry for any code mistakes, really long day and I'm headed to bed) doesn't work because in order to pass the varied return values around... the type can't change, etc.
The JSONResponseHandler
wrapper method actually works on my end but only with the interface {}
type given the variations in type... so I've left it out since it'd clutter up the code. However, if I use interface {}
on subsequent blocks with optional asterisked return properties (such as "NewMessageObject"), the JSON constructors seems to ignore those values since they're wrapped in an empty interface instead of just exposed as their raw type. However, they have to have a nil option....
What's wrong? The design in general? I'm basically trying to build a JSON object response (or return with a JSON-formatted error) through subsequent calls based on input data... in a neatly abstracted way.
答案1
得分: 1
要解决你的JSON问题,你可以使用一个结构体,并使用json标签为每个字段标记:"omitempty":
package main
import (
"encoding/json"
"fmt"
)
type foostruct struct {
Myfoo string `json:"myfoo,omitempty"`
Yourfoo string `json:"yourfoo,omitempty"`
Ourfoo string `json:"ourfoo,omitempty"`
}
func main() {
j := []byte(`{"myfoo":"mine", "yourfoo":"yours"}`)
fstruct := &foostruct{}
err := json.Unmarshal(j, fstruct)
if err != nil {
panic(err)
}
b, err := json.Marshal(fstruct)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
你会发现输出不包含字段"ourfoo":
{"myfoo":"mine","yourfoo":"yours"}
你可以在这里尝试运行代码:
http://play.golang.org/p/zKwFaxbLJu
英文:
To solve your json issue, you could use one struct and tag each field with json:"omitempty":
package main
import (
"encoding/json"
"fmt"
)
type foostruct struct {
Myfoo string `json:"myfoo,omitempty"`
Yourfoo string `json:"yourfoo,omitempty"`
Ourfoo string `json:"ourfoo,omitempty"`
}
func main() {
j := []byte("{\"myfoo\":\"mine\", \"yourfoo\":\"yours\"}")
fstruct := &foostruct{}
err := json.Unmarshal(j, fstruct)
if err != nil {
panic(err)
}
b, err := json.Marshal(fstruct)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
You'll see the the output does not contain the field "ourfoo":
{"myfoo":"mine","yourfoo":"yours"}
Try it out here:
http://play.golang.org/p/zKwFaxbLJu
答案2
得分: 0
我在我的应用程序中使用map而不是struct,特别是当我想要返回一个JSON响应给客户端时。这样可以给我更多的灵活性。请看一下这个例子:
package main
import (
"encoding/json"
"fmt"
)
func main() {
m := make(map[string]interface{})
m["appVersion"] = "0.0.1"
m["uploadStatus"] = "success"
strJson, _ := json.Marshal(m)
str := string(strJson)
fmt.Println("---ENCODING--")
fmt.Println(str)
//put it back
fmt.Println("---DECODING--")
n := make(map[string]interface{})
json.Unmarshal([]byte(str), &n)
for key, val := range n {
fmt.Println(key, ":", val)
}
}
输出结果为:
---ENCODING--
{"appVersion":"0.0.1","uploadStatus":"success"}
---DECODING--
appVersion : 0.0.1
uploadStatus : success
你可以在这里查看:http://play.golang.org/p/h4b4-nkZ4M
英文:
I use map instead of struct on my application, especially when I want to return back a json answer to client. Its give me more flexibility. Please take a look this :
package main
import (
"encoding/json"
"fmt"
)
func main() {
m := make(map[string]interface{})
m["appVersion"] = "0.0.1"
m["uploadStatus"] = "success"
strJson, _ := json.Marshal(m)
str := string(strJson)
fmt.Println("---ENCODING--")
fmt.Println(str)
//put it back
fmt.Println("---DECODING--")
n := make(map[string]interface{})
json.Unmarshal([]byte(str), &n)
for key, val := range n {
fmt.Println(key, ":", val)
}
}
and the output is :
---ENCODING--
{"appVersion":"0.0.1","uploadStatus":"success"}
---DECODING--
appVersion : 0.0.1
uploadStatus : success
You can check here : http://play.golang.org/p/h4b4-nkZ4M
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论