在Golang中从stdin读取json对象

How to read a json object from stdin? I want to copy and paste a json object into the stdin, read it and unmarshal it. Here is the code:

    var input string
    _, err := fmt.Scan(&input)
    if err != nil {
        fmt.Println(err)
        continue
    }

    var record MedicalRecord
    if err := json.Unmarshal([]byte(input), &record); err != nil {
        log.Println(err)
        continue
    }

And the errors are printed to console.

> 2018/06/26 00:26:32 invalid character ':' after top-level value
> 2018/06/26 00:26:32 unexpected end of JSON input
> 2018/06/26 00:26:32 invalid character ':' after top-level value
> 2018/06/26 00:26:32 invalid character ',' after top-level value
> 2018/06/26 00:26:32 invalid character ':' after top-level value
> 2018/06/26 00:26:32 invalid character ',' after top-level value
> 2018/06/26 00:26:32 invalid character ':' after top-level value
> 2018/06/26 00:26:32 invalid character ',' after top-level value
> 2018/06/26 00:26:32 invalid character ':' after top-level value
> 2018/06/26 00:26:32 invalid character ',' after top-level value

If I'm not mistaken, Go is reading until it finds ' ' . How can I solve this problem?

Use a *json.Decoder to consume JSON from an io.Reader:

package main

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

type MedicalRecord struct{}

func main() {
    var record MedicalRecord

    err := json.NewDecoder(os.Stdin).Decode(&record)
    if err != nil {
        log.Fatal(err)
    }
}

You can consume multiple consecutive JSON documents by calling Decode repeatedly:

package main

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

type MedicalRecord struct{}

func main() {
    var record MedicalRecord

    dec := json.NewDecoder(os.Stdin)
    for {
        err := dec.Decode(&record)
        if err == io.EOF {
            return
        }
        if err != nil {
            log.Fatal(err)
        }
    }
}

When you call json.Unmarshal you should have the full JSON object in hand, otherwise you're just trying to unmarshal some partial object that makes no sense to the parser.

  • Read from stdin into a buffer that is large enough to hold the entire JSON object.
  • Unmarshal the JSON object

For example:

package main

import (
    "bytes"
    "io"
    "fmt"
    "os"
    "bufio"
    "encoding/json"
)


func main() {

    var buf bytes.Buffer
    reader := bufio.NewReader(os.Stdin)

    for {
        line, err := reader.ReadString('
')
        if err != nil {
            if err == io.EOF {
                buf.WriteString(line)
                break // end of the input
            } else {
                fmt.Println(err.Error())
                os.Exit(1) // something bad happened
            }   
        }   
        buf.WriteString(line)

    }   

    fmt.Printf("valid json? %v
", json.Valid(buf.Bytes()))

    type MedicalRecord struct {
        Name string `json:"name"`
        Age int `json:"age"`
    }   

    var record MedicalRecord
    err := json.Unmarshal(buf.Bytes(), &record)
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1) // something bad happened
    }   

    fmt.Printf("name: %s, age: %d
", record.Name, record.Age)
}