日期输出从json.Encoder哪里来?

At the end of the JSON and Go blog post you'll find this sample program:

package main

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

func main() {
    dec := json.NewDecoder(os.Stdin)
    enc := json.NewEncoder(os.Stdout)
    for {
        var v map[string]interface{}
        if err := dec.Decode(&v); err != nil {
            log.Println(err)
            return
        }
        for k := range v {
            if k != "Name" {
                delete(v, k)
            }
        }
        if err := enc.Encode(&v); err != nil {
            log.Println(err)
        }
    }
}

I compiled this with go build json_decoder.go and then ran the program like so in bash:

echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | json_decoder

and received this output:

{"Name":"Wednesday"}
2019/08/17 22:09:20 EOF

The first line of output is exactly what I'd expect. But where is the line 2019/08/17 22:09:20 EOF coming from?

When EOF is reached the decoder returns io.EOF which is then is being output by the logger log.Println(err) with a timestamp prepended.

You can check for EOF when decoding like this for example

if err := dec.Decode(&v); err != nil {
    if err != io.EOF {
        log.Println(err)
    }
    return
}

Output:

➜ echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | ./json_decoder
{"Name":"Wednesday"}
log.Println(err)

That logger writes to standard error and prints the date and time of each logged message.


You are seeing both stdoutand stderr so:

  1. The solution is to redirect the stdout to a file (or to another pipe):

    echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | ./jsonio > file.json
    

    Output (which is stderr):

    2019/08/17 20:35:40 EOF
    

    Then see the file content (which is stdout):

    cat file.json 
    
    {"Name":"Wednesday"}
    

  1. You may discard stderr (not recommended, but for test purpose) or redirect stderr:

    echo '{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}' | ./jsonio 2>/dev/null
    

    Output (which is stdout):

    {"Name":"Wednesday"}
    

  1. Or don't show the EOF at all (note: you'll see other errors like incorrect JSON errors in stderr):

    package main
    
    import (
        "encoding/json"
        "io"
        "log"
        "os"
    )
    
    func main() {
        dec := json.NewDecoder(os.Stdin)
        enc := json.NewEncoder(os.Stdout)
        for {
            var v map[string]interface{}
            if err := dec.Decode(&v); err != nil {
                if err != io.EOF {
                    log.Println(err)
                }
                return
            }
            for k := range v {
                if k != "Name" {
                    delete(v, k)
                }
            }
            if err := enc.Encode(&v); err != nil {
                log.Println(err)
            }
        }
    }