如何在 go 中跳过 json.Marshal 字段而不是 json.Unmarshal 字段?

问题描述

type Alpha struct { 
  Name            string `json:"name"`
  SkipwhenMarshal string `json:"skipwhenMarshal"`
}

func MarshalJSON(out interface{}){
  json.Marshal(out)
} 

在执行 json.Marshal 时是否可以忽略 SkipwhenMarshal 字段,但在执行时不能 json.解组。 它应该适用于任何调用 MarshalJSON

的类型

解决方法

诸如“omitempty”和“-”之类的字段标记修饰符适用于编组和解组,因此没有自动方法。

您可以为您的类型实现一个 MarshalJSON,忽略您需要的任何字段。无需实现自定义解组器,因为默认设置适合您。

例如像这样:

type Alpha struct {
    Id              int32
    Name            string
    SkipWhenMarshal string
}

func (a Alpha) MarshalJSON() ([]byte,error) {
    m := map[string]string{
        "id":   fmt.Sprintf("%d",a.Id),"name": a.Name,// SkipWhenMarshal *not* marshaled here
    }

    return json.Marshal(m)
}

您还可以通过使用别名类型使其更简单:

func (a Alpha) MarshalJSON() ([]byte,error) {
    type AA Alpha
    aa := AA(a)
    aa.SkipWhenMarshal = ""

    return json.Marshal(aa)
}

此处将输出 SkipWhenMarshal,但其值已清零。这种方法的优点是,如果 Alpha 有很多字段,您不必重复它们。

,

你想要的根本无法通过 encoding/json 来完成。

但是你可以有两种类型

type AlphaIn struct { 
    Name string `json:"name"`
    Bar  string `json:"skipWhenMarshal"`
}

type AlphaOut struct { 
    Name string `json:"name"`
    Bar  string `json:"-"`
}

使用 AlphaIn 使用 encoding/json.Unmarshal 反序列化 JSON,并使用 AlphaOut 使用 encoding/json.Marshal 将结构序列化为 JSON。

现在单独使用这将是绝对痛苦的,但是:结构标记在类型之间的可转换性中不起作用,这使您可以通过简单的类型转换从 AlphaIn 转换为 AlphaOut:

var a AlphaIn = ...
var b AlphaOut = AlphaOut(a)

(更合理的命名方案应该是 AlphaAlphaToJSON 或类似的东西。)

,

我会像这样编写一个自定义的 marshal 函数:playground

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
    "strings"
)

type Alpha struct {
    Name            string `json:"name"`
    SkipWhenMarshal string `json:"SkipWhenMarshal,skip"`
}

func main() {
    var a Alpha
    a.Name = "John"
    a.SkipWhenMarshal = "Snow"

    out,_ := marshal(a)

    fmt.Println(string(out))

    var b Alpha
    json.Unmarshal([]byte(`{"Name":"Samwell","SkipWhenMarshal":"Tarly"}`),&b)

    fmt.Println(b)
}

// custom marshaling function for json that accepts an additional tag named skip to ignore the field
func marshal(v Alpha) ([]byte,error) {
    m := map[string]interface{}{}

    ut := reflect.TypeOf(v)
    uv := reflect.ValueOf(v)

    for i := 0; i < ut.NumField(); i++ {
        field := ut.Field(i)
        js,ok := field.Tag.Lookup("json")
        if !ok || !strings.Contains(js,"skip") {
            intf := uv.Field(i).Interface()
            switch val := intf.(type) {
            case int,int8,uint8:
                m[field.Name] = val
            case string:
                m[field.Name] = val
            }

        }
    }

    return json.Marshal(m)
}

它基本上将结构重建为没有跳过字段的映射,并将其传递给原始 Marshal 函数。

现在只需将 SKIP 标签添加到任何字段,它就可以工作了。

你只需要改进这部分:

switch val := intf.(type) {

因为这仅假设您只有字符串或整数作为字段。处理更多类型会更理想。

,

你可以试试这个,即将结构分解成组件 -> 组合

解组时使用 Beta 类型。它只会解组 Beta 结构中定义的字段

type Beta struct {
  Name            string `json:"name"`
}

type Alpha struct {
    *Beta
    SkipWhenMarshal string `json:"skipWhenMarshal"`
}