我有一个Type" Book"我从一个返回json的不同界面读取。在阅读json并处理数据后,我必须将书籍转换为公共书籍类型以隐藏字段并更改输出格式。
我的问题是,来自同一字段(ISBN)的输入类型有时是字符串,有时是int。我认为最简单的解决方案是使用json.Number来解组数据。这工作 - 但我需要在不同领域的传出json上的字符串......
这就是我需要帮助的地方。我会有一个自定义类型,我可以在字段的公共结构中设置,我想将output-json-field设置为字符串。我将自定义类型命名为#34; mytype"在示例中。 (实际数据是嵌套的,我有更多的字段,我在输出中设置为字符串 - 公共结构中的id字段只是一个测试)
我的意思是,它应该看起来像那样 - 或者不是吗?
func (m *mytype) MarshalJSON() ([]byte, error) {
...
}
以下是我的示例代码:https://play.golang.org/p/rS9HddzDMp
package main
import (
"encoding/json"
"fmt"
"bytes"
)
/* ----------------------------------------------------------------------------------------------------
Definition of the internal Book object (read from input)
-----------------------------------------------------------------------------------------------------*/
type Book struct {
Id json.Number `json:"id"`
Revision int `json:"revision"`
ISBN json.Number `json:"isbn"`
Title string `json:"title"`
}
/* ----------------------------------------------------------------------------------------------------
Definition of the public Book object
-----------------------------------------------------------------------------------------------------*/
type AliasBook Book
type omit *struct{}
type mytype string
type PublicBook struct {
Id string `json:"id"`
Revision omit `json:"revision,omitempty"`
ISBN mytype `json:"isbn"`
*AliasBook
}
/* ----------------------------------------------------------------------------------------------------
Rendering functions
-----------------------------------------------------------------------------------------------------*/
func (bb *Book) MarshalJSON() ([]byte, error) {
fmt.Println("---------------MarschalJSON---------------")
aux := PublicBook{
Id: bb.Id.String(),
AliasBook: (*AliasBook)(bb),
}
return json.Marshal(&aux)
}
func main() {
var jsonStreams[2][]byte
// Input ISBN as string
jsonStreams[0] = []byte(`{"id":"123","revision":1234,"isbn":"978-3-86680-192-9","title":"Go for dummies"}`)
// Input ISBN as int
jsonStreams[1] = []byte(`{"id":123,"revision":1234,"isbn":9783866801929,"title":"Go for dummies"}`)
// For each stream
for i := range jsonStreams {
fmt.Print("stream: ")
fmt.Println(string(jsonStreams[i]))
// Read Input
b := Book{}
err := json.Unmarshal(jsonStreams[i], &b)
if err == nil {
fmt.Printf("%+v\n", b)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", b)
}
// Output as JSON
response := new(bytes.Buffer)
enc := json.NewEncoder(response)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
err = enc.Encode(&b)
if err == nil {
fmt.Printf("%+v\n", response)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", response)
}
}
}
修改 我有一个适合我的解决方案。 https://play.golang.org/p/Vr4eELsHs1
关键点是,我必须采用" fmt.Sprint(* isbn)来返回封送器中的字符串。我创建了一个新类型,使用json.Number函数将输入转换为int64,并将其与json自定义封送器转换为字符串。
答案 0 :(得分:1)
最简单的解决方案是拥有一个代表ISBN号的自定义类型。然后,您可以实现自定义JSON解码功能,以便您可以解析字符串和放大器。数字输入。例如
type isbn string
func (s *isbn) UnmarshalJSON(buf []byte) error {
// Read numeric characters only from raw JSON input. This will handle strings, numbers or null etc and strip any
// optional separators.
out := make([]byte, 0, len(buf))
for _, b := range buf {
if b >= '0' && b <= '9' {
out = append(out, b)
}
}
// Validate ISBN (assuming not using old 10 digit ISBN)
l := len(out)
if l != 13 {
return errors.New("Invalid ISBN length")
}
// Calculate check digit and ensure valid.
// Create formatted output. This assumes 13 characters for simplicity
*s = isbn(fmt.Sprintf("%s-%s-%s-%s-%s", out[:3], out[3:4], out[4:9], out[9:12], out[12:]))
return nil
}
以上只是以适合输出的格式存储ISBN。但是,如果需要,您可以以任何格式存储并具有单独的json.Marshaler实现来格式化输出。
然后,您可以像往常一样在Book
中创建一个字段:
type Book struct {
Id json.Number `json:"id"`
Revision int `json:"revision"`
ISBN isbn `json:"isbn"`
Title string `json:"title"`
}
上面的ISBN解码示例仅供参考。您应该创建一个经过单元测试的完整实现,以确保它正确处理所有预期的输入,并在空/格式错误的输入上引发相应的错误。如果这是一个问题,也可以提高性能。
修改强>
您无法使用相同的变量在json.Marshal
实施中调用json.Marshaler
。这将导致无限递归循环,例如
json.Marshal(e)
- &gt; e.MarshalJSON
- &gt; json.Marshal(e)
- &gt; e.MarshalJSON
...
json.Number
类型是数字的字符串表示形式。如果您只是想将所有数字输出为字符串,则根本不需要任何自定义类型。只需在代码中使用相关的字符串值即可。例如:
type PublicBook struct {
Id string `json:"id"`
// all other fields...
}
// Creates the public book representation from a book
func Public(b *Book) *PublicBook {
return &PublicBook{
Id: string(b.Id),
}
}
这将始终输出一个字符串,因为您使用的是string
类型,而不是具有自定义JSON编组/解组实现的json.Number
类型。
答案 1 :(得分:0)
我有一个适合我的解决方案。 https://play.golang.org/p/Vr4eELsHs1
关键点是,我必须使用fmt.Sprint(*isbn)
返回封送器中的字符串。我创建了一个新类型,使用json.Number函数将输入转换为int64,并将其与json自定义封送程序转换为字符串。
感谢您的帮助!