How to unmarshal a field that can be an array or a string in Go?

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-20 05:38:15

问题


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:


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
    }
}


来源:https://stackoverflow.com/questions/56377551/how-to-unmarshal-a-field-that-can-be-an-array-or-a-string-in-go

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!