如何使用Go从JSON文件读取一个值

如何使用Go从JSON文件读取一个值

问题描述:

I an new to programming in Go so apologies if this is something obvious. I have a JSON file named foo.json:

{"type":"fruit","name":"apple","color":"red"}

and I am writing a Go program that has to do something when the "name" value in the JSON file is "apple". It needs no other information from that JSON file as that file is used for a completely different purpose in another area of the code.

I have read documentation on Decode() and Unmarshal() and abut 30 different web pages describing how to read the whole file into structures, etc. but it all seems extremely complicated for what I want to do which is just write the correct code to implement the first 2 lines of this pseudo-code:

file, _ := os.Open("foo.json")
name = file["name"]
if (name == "apple") {
    do stuff
}

such that I end up with a Go variable named name that contains the string value apple. What is the right way to do this in Go?

Go code>中编程的新手,如果这很明显,我们深表歉意。 我有一个名为 foo.json code>的 JSON code>文件: p>

  {“ type”:“ fruit”,“ name”  :“ apple”,“ color”:“ red”} 
  code>  pre> 
 
 

,而我正在编写一个 Go code>程序,该程序必须在 JSON文件中的“名称”值是“苹果”。 它不需要来自该JSON文件的其他信息,因为该文件在代码的另一个区域用于完全不同的用途。 p>

我已阅读了Decode()和Unmarshal()的文档, 紧靠30个不同的网页,它们描述了如何将整个文件读入结构等,但是对于我想做的事情来说,这似乎非常复杂,只需编写正确的代码以实现此伪代码的前两行即可: p>

  file,_:= os.Open(“ foo.json”)
name = file [“ name”] 
if(name ==“ apple”){
做 东西
} 
  code>  pre> 
 
 

这样我最终得到一个名为 name code>的 Go code>变量,该变量包含字符串 值 apple code>。 在Go中执行此操作的正确方法是什么? p> div>

The easiest method to do what you want is to decode into a struct.

Provided the format remains similar to {"type":"fruit","name":"apple","color":"red"}

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

var data []byte
data, _ = ioutil.ReadFile("foo.json")

var str Name
_ = json.Unmarshal(data, &str)

if str.Name == "apple" {
    // Do Stuff
}

Your other option is to use third party libraries such as gabs or jason.

Gabs :

jsonParsed, err := gabs.ParseJSON(data)
name, ok := jsonParsed.Path("name").Data().(string)

Jason :

v, _ := jason.NewObjectFromBytes(data)
name, _ := v.GetString("name")

Update :

The structure

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

is the json equivalent of {"name":"foo"}.

So unmarshaling won't work for the following json with different formats.

[{"name":"foo"}]

{"bar":{"name":"foo"}}

PS : As mentioned by W.K.S. In your case an anonymous struct would suffice since you're not using this structure for anything else.

One thing is reading a file and other one is decoding a JSON document. I leave you a full example doing both. To run it you have to have a file called file.json in the same directory of your code or binary executable:

package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
    "os"
)

func main() {
    f, err := os.Open("file.json") // file.json has the json content
    if err != nil {
        log.Fatal(err)
    }

    bb, err := ioutil.ReadAll(f)
    if err != nil {
        log.Fatal(err)
    }

    doc := make(map[string]interface{})
    if err := json.Unmarshal(bb, &doc); err != nil {
        log.Fatal(err)
    }

    if name, contains := doc["name"]; contains {
        log.Printf("Happy end we have a name: %q
", name)
    } else {
        log.Println("Json document doesn't have a name field.")
    }

    log.Printf("full json: %s", string(bb))
}

https://play.golang.org/p/0u04MwwGfn

I have also tried to find a simple solution such as $d = json_decode($json, true) in PHP and came to the conclusion that there is no such simple way in Golang. The following is the simplest solution I could make (the checks are skipped for clarity):

var f interface{}
err = json.Unmarshal(file, &f)

m := f.(map[string]interface{})
if (m["name"] == "apple") {
  // Do something
}

where

  • file is an array of bytes of JSON string,
  • f interface serves as a generic container for unknown JSON structure,
  • m is a map returned by the type assertion.

We can assert that f is a map of strings, because Unmarshal() builds a variable of that type for any JSON input. At least, I couldn't make it return something different. It is possible to detect the type of a variable by means of run-time reflection:

fmt.Printf("Type of f = %s
", reflect.TypeOf(f))

For the f variable above, the code will print Type of f = map[string]interface {}.

Example

And this is the full code with necessary checks:

package main

import (
  "fmt"
  "os"
  "io/ioutil"
  "encoding/json"
)

func main() {
  // Read entire file into an array of bytes
  file, err := ioutil.ReadFile("foo.json")
  if (err != nil) {
    fmt.Fprintf(os.Stderr, "Failed read file: %s
", err)
    os.Exit(1)
  }

  var f interface{}
  err = json.Unmarshal(file, &f)
  if (err != nil) {
    fmt.Fprintf(os.Stderr, "Failed to parse JSON: %s
", err)
    os.Exit(1)
  }

  // Type-cast `f` to a map by means of type assertion.
  m := f.(map[string]interface{})
  fmt.Printf("Parsed data: %v
", m)

  // Now we can check if the parsed data contains 'name' key
  if (m["name"] == "apple") {
    fmt.Print("Apple found
")
  }
}

Output

Parsed data: map[type:fruit name:apple color:red]
Apple found

The proper Go way of doing this would be to decode into an instance of an anonymous struct containing only the field you need.

func main() {
    myStruct := struct{ Name string }{}
    json.Unmarshal([]byte(`{"type":"fruit","name":"apple","color":"red"}`), &myStruct)
    fmt.Print(myStruct.Name)
}

Playground Link

Alternatively, You could use Jeffails/gabs JSON Parser:

jsonParsed,_ := gabs.ParseJSON([]byte(`{"type":"fruit","name":"apple","color":"red"}`));
value, ok = jsonParsed.Path("name").Data().(string)