dongzaobei0942
2019-05-30 11:35 阅读 59

如何在Go中解组可以是数组或字符串的字段?

I'm trying to unmarshal this file:

{
  "@babel/code-frame@7.0.0": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel/tree/master/packages/babel-code-frame",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/Users/lislab/workspace/falcon-enrolment/frontend-customer/node_modules/@babel/code-frame",
    "licenseFile": "/Users/lislab/workspace/falcon-enrolment/frontend-customer/node_modules/@babel/code-frame/LICENSE"
  },
  "json-schema@0.2.3": {
    "licenses": [
      "AFLv2.1",
      "BSD"
    ],
    "repository": "https://github.com/kriszyp/json-schema",
    "publisher": "Kris Zyp",
    "path": "/Users/lislab/workspace/falcon-enrolment/frontend-customer/node_modules/json-schema",
    "licenseFile": "/Users/lislab/workspace/falcon-enrolment/frontend-customer/node_modules/json-schema/README.md"
  }
}

into this struct:

type Dependency struct {
    Name    string
    URL     string
    Version string
    License string
}

using these instructions:

dependencies := map[string]*json.RawMessage{}
err = json.Unmarshal(file, &dependencies)
// boilerplate

for key, value := range dependencies {
    depVal := map[string]string{}
    err = json.Unmarshal(*value, &depVal)
    // boilerplate
    result = append(result, depVal)
}

The problem with this is that in "json-schema@0.2.3" we have an array of licenses instead of a string, and due to that I obviously get

json: cannot unmarshal array into Go value of type string 

Is there a way to automatically deal with the field license which can be an array or a string?

Thanks

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

1条回答 默认 最新

  • 已采纳
    duanhuai7532 duanhuai7532 2019-05-30 11:54

    Unfortunately, there is no real automatic solution for this provided by the json package.

    But you can unmarshal the dependencies to a map[string]*json.RawMessage instead of map[string]string. json.RawMessage is just a []byte, so you can decide on the type of the message based on the first byte.

    Example:

    for _, value := range dependencies {
        depVal := map[string]*json.RawMessage{}
    
        _ = json.Unmarshal(*value, &depVal)
    
        // check if the first character of the RawMessage is a bracket
        if rune([]byte(*depVal["licenses"])[0]) == '[' {
            var licenses []string
            json.Unmarshal(*depVal["licenses"], &licenses)
            fmt.Println(licenses)
            // do something with the array
        }
    
        result = append(result, Dependency{
            URL:     string(*depVal["repository"]),
            License: string(*depVal["licenses"]),
        })
    }
    

    Another solution would be to use 2 structs. One contains the dependencies as string, the other as array. You can then try to call json.Unmarshal on both of them. Example:

    
    type Dependency struct {
        Licenses string
        // other fields
    }
    
    type DependencyWithArr struct {
        Licenses []string
        // other fields
    }
    
    // in your function
    for _, value := range dependencies {
        type1 := Dependency{}
        type2 := DependencyWithArr{}
    
        err = json.Unmarshal(*value, &type1)
        if err != nil {
            err = json.Unmarshal(*value, &type2)
            // use the array type
        } else {
            // use the single string type
        }
    }
    
    点赞 评论 复制链接分享

相关推荐