Golang中如何序列化与反序列化多种不同的JSON结构体

Golang中如何序列化与反序列化多种不同的JSON结构体 我想将多个不同的结构体存储在JSON文件中,以便读取并执行特定操作。其中一个结构体包含多个字段,例如:

type copyFile struct {
    Src string            `json:"src"`
    Dst string            `json:"dst"`
    Sudo bool             `json:"sudo"`
    OverWrite bool        `json:"overwrite"`
}

另一个结构体仅包含用户命令,例如:

type userCommand struct {
    Cmd string          `json:"cmd"`
}

如果我将它们都放入数组并使用json.Marshal处理,会得到类似这样的结果:

[{“cmd”:“echo user command”},{“src”:"/dir/src",“dst”:"/dir/dst",“sudo”:true,“overwrite”:false}]

应该如何对这个结果进行Unmarshal?

目前我已经将copyFile结构体改为:

type copyFile struct {
    Cmd string         `json:"copy"`
}

然后使用分隔符连接src、dst、sudo和overwrite字段,这样我就可以通过以下方式对整个内容进行Unmarshal:

x, _ := json.Marshal(commandsArray)
var data []map[string]string
err := json.Unmarshall(x,&data)

这种方法有效,我只需要对copyFile的Cmd字段使用strings.Split()即可。但我不知道是否还有其他方法可以保持结构体的完整性。

有人有什么建议吗?我是Go语言的新手,目前还在摸索中。

谢谢


更多关于Golang中如何序列化与反序列化多种不同的JSON结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

可以使用一个非常简单的结构体

typedef cmd struct {
   Cmd string `json:"cmd"`
   Args []string `json:"args"`
}

更多关于Golang中如何序列化与反序列化多种不同的JSON结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


即使我将当前的 copyFile 结构保留为单个 Cmd 字符串,就像 userCommand 一样,我也会将它们分开处理。部分原因是我可以通过 JSON 文件中的映射键来"标记"要执行的操作。例如,如果 copyFile 是一个带有分隔符的单个字符串(本例中使用"%"),我最终会得到:

[{"copy":"/dir/src%/dir/dst%true%false"},{"cmd":"echo user command"}]

在解组数据之后,我可以根据键来判断要执行什么操作。如果所有对象都用相同的结构表示,那么(至少据我所知)就没有办法对它们进行不同的标记。

谢谢。

可以使用Go的json.RawMessage来处理多种不同结构的JSON数组。这种方法能够保持每个结构体的完整性,不需要修改原始结构体定义。

首先定义原始结构体:

type copyFile struct {
    Src       string `json:"src"`
    Dst       string `json:"dst"`
    Sudo      bool   `json:"sudo"`
    OverWrite bool   `json:"overwrite"`
}

type userCommand struct {
    Cmd string `json:"cmd"`
}

然后创建包装器类型来处理混合JSON数组:

type CommandWrapper struct {
    Type string          `json:"type"` // 用于标识命令类型
    Data json.RawMessage `json:"data"` // 存储原始JSON数据
}

序列化示例:

func serializeCommands(commands []interface{}) ([]byte, error) {
    var wrappers []CommandWrapper
    
    for _, cmd := range commands {
        var wrapper CommandWrapper
        var rawData []byte
        var err error
        
        switch v := cmd.(type) {
        case copyFile:
            wrapper.Type = "copyFile"
            rawData, err = json.Marshal(v)
        case userCommand:
            wrapper.Type = "userCommand" 
            rawData, err = json.Marshal(v)
        default:
            return nil, fmt.Errorf("unknown command type")
        }
        
        if err != nil {
            return nil, err
        }
        wrapper.Data = rawData
        wrappers = append(wrappers, wrapper)
    }
    
    return json.Marshal(wrappers)
}

反序列化示例:

func deserializeCommands(data []byte) ([]interface{}, error) {
    var wrappers []CommandWrapper
    err := json.Unmarshal(data, &wrappers)
    if err != nil {
        return nil, err
    }
    
    var commands []interface{}
    for _, wrapper := range wrappers {
        switch wrapper.Type {
        case "copyFile":
            var cf copyFile
            err := json.Unmarshal(wrapper.Data, &cf)
            if err != nil {
                return nil, err
            }
            commands = append(commands, cf)
        case "userCommand":
            var uc userCommand
            err := json.Unmarshal(wrapper.Data, &uc)
            if err != nil {
                return nil, err
            }
            commands = append(commands, uc)
        default:
            return nil, fmt.Errorf("unknown command type: %s", wrapper.Type)
        }
    }
    
    return commands, nil
}

使用示例:

// 创建混合命令数组
commands := []interface{}{
    userCommand{Cmd: "echo user command"},
    copyFile{
        Src:       "/dir/src",
        Dst:       "/dir/dst", 
        Sudo:      true,
        OverWrite: false,
    },
}

// 序列化
data, err := serializeCommands(commands)
if err != nil {
    log.Fatal(err)
}

// 反序列化  
decodedCommands, err := deserializeCommands(data)
if err != nil {
    log.Fatal(err)
}

// 使用命令
for _, cmd := range decodedCommands {
    switch v := cmd.(type) {
    case userCommand:
        fmt.Printf("User command: %s\n", v.Cmd)
    case copyFile:
        fmt.Printf("Copy file from %s to %s\n", v.Src, v.Dst)
    }
}

这种方法保持了每个结构体的完整性,不需要对字段进行字符串分割操作。

回到顶部