将JSON反序列化为UUID类型

6

我正在尝试使用json.Unmarshaler接口将UUID解组为结构体上的uuid.UUID字段。我创建了一个名为myUUID的自定义类型,一切正常,直到我尝试访问通常在uuid.UUID上的方法时出现问题。我该怎么处理?我对Go相对较新,所以也许我还没有完全理解自定义类型。

package main

import (
    "encoding/json"
    "errors"
    "fmt"

    "code.google.com/p/go-uuid/uuid"
)

var jsonstring = `
{
    "uuid": "273b62ad-a99d-48be-8d80-ccc55ef688b4"
}
`

type myUUID uuid.UUID

type Data struct {
    uuid myUUID
}

func (u *myUUID) UnmarshalJson(b []byte) error {
    id := uuid.Parse(string(b[:]))
    if id == nil {
            return errors.New("Could not parse UUID")
    }
    *u = myUUID(id)
    return nil
}

func main() {
    d := new(Data)
    err := json.Unmarshal([]byte(jsonstring), &d)
    if err != nil {
            fmt.Printf("%s", err)
    }
    fmt.Println(d.uuid.String())
}
3个回答

8
你可能需要确保myuuid变量在Data struct中可见/导出:即“public”。 类型别名MyUUID也需要如此(而不是myUUID)。
type MyUUID uuid.UUID

type Data struct {
    Uuid MyUUID
}

来自JSON和Go:

json包只能访问结构类型的公开字段(即以大写字母开头的字段)。


正如Ainar G所评论的一样,样式指南也建议:

名称中的首字母缩略词或首字母缩写词(例如“URL”或“NATO”)应具有一致的大小写。
例如,“URL”应显示为“URL”或“url”(例如“urlPony”或“URLPony”),而不是“Url”。这是一个例子:ServeHTTP应该写成ServeHTTP而不是ServeHttp

当“ID”缩写表示“标识符”时,此规则也适用,因此请写“appID”而不是“appId”。

对于您的情况,这意味着:

type Data struct {
    UUID MyUUID
}

1
@Ainar-G 确实。我已经在答案中包含了该命令以增加可见性。 - VonC
如果我想将uuid设置为私有属性,那应该命名为uUID?同样的,uRLhTTPStatus也是如此吗? - warvariuc
1
@warvariuc,除了 OP 的情况需要公开以便通过 JSON 访问之外,不需要 uuidhttpStatus(大小写需保持一致)。 - VonC
谢谢解释。我明白了。 - warvariuc
看起来Go的自定义类型不会继承“父”类型的方法。我改变了代码的结构来解决这个问题,并创建了一个自定义的UnmarshalJSON方法来处理它。 - Kyle
显示剩余11条评论

6

Go语言的自定义类型不继承方法。我使用了一个自定义结构体并附加了一个UnmarshalJSON来重构代码。

import (
        "errors"
        "strings"

        "github.com/pborman/uuid"
)


type ServiceID struct {
    UUID uuid.UUID
}

type Meta struct {
    Name    string `json:"name"`
    Version string `json:"version"`
    SID     *ServiceID `json:"UUID"`
}

func (self *ServiceID) UnmarshalJSON(b []byte) error {
    s := strings.Trim(string(b), "\"")
    self.UUID = uuid.Parse(s)
    if self.UUID == nil {
            return errors.New("Could not parse UUID")
    }
    return nil
}

这怎么可能工作或编译呢?在类型ServiceId UUID中不是指针,那么if self.UUID == nil {怎么会编译通过呢? - Emaborsa
@emaborsa 因为 uuid.UUID 实际上是 []byte,而切片可以为 nil。请参见:https://github.com/pborman/uuid/blob/master/uuid.go#L32 - Kyle
实际上,我遇到了两个编译错误。第一个是在Parse函数上出现的“无法将2个值分配给1个变量”错误,第二个是在self.UUID == nil处出现的“不能将nil(未定义类型的nil值)转换为uuid.UUID”的错误。第一个问题可以通过添加第二返回值来简单地解决,或者像这样跳过它:self.UUID,_ = uuid.Parse(s)。对于第二个问题,我必须将UUID属性定义为指针,如UUID *uuid.UUID,但是然后Parse函数的分配会报错。 - Emaborsa
@Emaborsa 我猜你正在使用的UUID包是github.com/google/uuid,它确实从其Parse函数返回2个参数,并使用缓冲数组(不可为空)来存储UUID字节。然而,原问题涉及到code.google.com/p/go-uuid/uuid,现在已经变成了github.com/pborman/uuid,并且从Parse中不返回error值,使用的是未缓冲的字节切片(可为空)。我将更新答案以包括所需的导入以支持答案,这样就可以编译了。 - Kyle

2
我已经尝试修改上述解决方案,以提供一个满足以下要求的解决方案:
  1. 与Google UUID库兼容
  2. 可以编译通过
  3. 生成一个带有规范UUID格式273b62ad-a99d-48be-8d80-ccc55ef688b4的JSON字符串
package main

import (
    "encoding/json"
    "fmt"
    "github.com/google/uuid"
)

var jsonstring = `
{
    "uuid": "273b62ad-a99d-48be-8d80-ccc55ef688b4"
}
`

type myUUID uuid.UUID

type Data struct {
    Uuid myUUID  `json:"uuid"`
}

func (u *myUUID) UnmarshalJSON(b []byte) error {
    id, err := uuid.Parse(string(b[:]))
    if err != nil {
            return err
    }
    *u = myUUID(id)
    return nil
}

func (u *myUUID) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf("\"%s\"", uuid.UUID(*u).String())), nil
}

func main() {
    d := new(Data)
    err := json.Unmarshal([]byte(jsonstring), &d)
    if err != nil {
        panic(err)
    }
    // Output: [39 59 98 173 169 157 72 190 141 128 204 197 94 246 136 180]
    fmt.Println(d.Uuid)
    s, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }
    // Output: {"uuid":"273b62ad-a99d-48be-8d80-ccc55ef688b4"}
    fmt.Println(string(s))
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接