J'ai un court programme Go qui exécute la commande go list -json
pour plusieurs packages, stocke la sortie de chaque exécution de la commande dans un json.RawMessage, ajoute chaque json.RawMessage dans une tranche de json.RawMessages, puis renvoie le résultat au serveur après avoir concaténé chacun des json.RawMessages et compacté le json. Cependant, un message d'erreur est généré lorsque j'exécute json.Compact
et je ne parviens pas à localiser la source. Googler ce message d'erreur révèle que la plupart des gens qui semblent le rencontrer - que ce soit pour un ,
invalide ou un autre caractère - ont du mal à en trouver la source.
invalid character ',' looking for beginning of value
Le code avec des commentaires est disponible ici sur play.golang.org (bien qu'il ne soit pas lu ici) et aussi ci-dessous.
Question: pouvez-vous expliquer la source de cette erreur et comment l’éviter?
(Remarque, certains des packages ont été inclus uniquement à des fins de test)
package main
import (
"expvar"
"encoding/json"
"bytes"
"fmt"
"github.com/go-martini/martini"
"github.com/zenazn/goji"
"github.com/zenazn/goji/web"
"go/build"
"log"
"math/Rand"
"net/http"
_ "net/http/pprof"
"os/exec"
)
type myType struct {
J []json.RawMessage
}
var pack map[string]string
type GoList struct {
Imports []string
}
type Import struct {
Dir string
ImportPath string
Name string
Target string
Standard bool
Root string
GoFiles []string
Imports []string
Deps []string
}
const contentTypeJSON = "application/json"
func main() {
http.HandleFunc("/importgraph", func(w http.ResponseWriter, r *http.Request) { importGraph(w, r) })
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Inside handler")
fmt.Fprintf(w, "Hello world from my Go program!")
}
func importGraph(w http.ResponseWriter, r *http.Request) {
pack = make(map[string]string)
var t myType
cmd := exec.Command("go", "list", "-json")
stdout, err := cmd.Output()
if err != nil {
println(err.Error())
return
}
var list GoList
err = json.Unmarshal(stdout, &list)
for _, d := range list.Imports {
//get the imports for each of the packages listed by go list -json
t.imports(d)
}
var buff bytes.Buffer
//concatenate the separate json.RawMessages together into json
buff.WriteByte('[')
for i, j := range t.J {
if i != 0 {
buff.WriteByte(',')
}
buff.Write([]byte(j))
}
buff.WriteByte(']')
var buffer bytes.Buffer
if err := json.Compact(&buffer, buff.Bytes()); err != nil {
println(err.Error()) //error message: invalid character ',' looking for beginning of value
return
}
w.Header().Set("Content-Type", contentTypeJSON)
w.Write(buffer.Bytes())
}
func (myObj *myType) imports(pk string) error {
cmd := exec.Command("go", "list", "-json", pk)
stdout, _ := cmd.Output()
pack[pk] = pk
var deplist Import
json.Unmarshal(stdout, &deplist)
var newj json.RawMessage
json.Unmarshal(stdout, &newj)
myObj.J = append(myObj.J, newj)
for _, imp := range deplist.Imports {
if _, ok := pack[imp]; !ok {
myObj.imports(imp) //recursive call to get the imports of the imports etc
}
}
return nil
}
Tout d’abord, comme cela a été commenté, êtes-vous sûr de ne pas pouvoir utiliser Le package go/build
directement plutôt que d’exécuter go list
?
Je n'utiliserais pas println
(ou fmt.Println
) dans les gestionnaires HTTP. Il est bien préférable d'utiliser log.Println
et/ou d'obtenir l'erreur dans la variable ResponseWriter
. En outre, il est judicieux d’emballer votre appel ListenAndServe
avec log.Fatal
.
Lors de l'impression/de la journalisation des valeurs error
, vous pouvez simplement utiliser err
, il n'est pas nécessaire d'avoir err.Error()
.
En outre, lorsque vous souhaitez réellement faire quelque chose de plus détaillé que le simple signalement/enregistrement du message d'erreur, vous pouvez consulter son type et d'autres informations. Par exemple, log.Printf("verbose error info: %#v", err)
donne:
&json.SyntaxError{msg:"invalid character ',' looking for beginning of value", Offset:0}
J'ai essayé cela parce que je sais que le paquetage json
renvoie divers types d'erreur avec des informations supplémentaires et j'espérais que la valeur de décalage serait utile. Si cela avait été le cas, cela aurait pu être utile:
if err := json.Compact(…) {
if err != nil {
log.Println("json.Compact:", err)
if serr, ok := err.(*json.SyntaxError); ok {
log.Println("Occurred at offset:", serr.Offset)
// … something to show the data in buff around that offset …
}
}
}
Mais le décalage zéro n'est pas utile :(
Donc, bien que cela n'identifie pas votre problème, espérons-leil peut être utile pour votre enquête ultérieure.
Modifier:
Donc, après avoir ajouté:
log.Println("Write file:", ioutil.WriteFile("data.json", buff.Bytes(), 0600))
j'ai ensuite exécuté un validateur JSON sur le fichier résultant du bloc de traitement des erreurs ci-dessus et identifié cette pièce:
"XTestImports": [
"io",
"log",
"net"
]
},,{
"Dir": "/usr/local/go/src/mime",
"ImportPath": "mime",
"Name": "mime",
Notez le double ,,
.
Cela devrait vous indiquer que l’erreur dans votre code est . Sinon, vous devez ignorer les entrées vides, que ce soit lors du traitement de t.J
ou lors de sa création. Ce dernier est meilleur et implique simplement:
if len(newj) > 0 {
myObj.J = append(myObj.J, newj)
}
(où btw vous ne vérifiez pas les erreurs de json.Unmarshal
donc il n'est pas clair si cela est supposé être vide ou s'il est vide en raison d'une erreur précédente. Jamais erreur renvoyée!)
J'ai également rencontré le même message d'erreur dans un programme Go, mais le message d'erreur se trouvait dans l'erreur de réponse HTTP, au format HTML, lorsque mon analyseur de réponse HTTP attendait JSON.
Pour moi, la solution a été de changer ma demande pour inclure la définition de l'en-tête Content-Type
à application/json
. La procédure à suivre dépend de la bibliothèque cliente http que vous utilisez. Si vous avez accès au type de noyau http.Header
, vous pouvez définir l'en-tête avec .Set(...)
.
Je me rends compte que la portée de ce correctif pour moi peut ne pas s'appliquer à la question initiale, mais je suis arrivé ici le premier après avoir cherché sur Google et je pensais que cela aiderait les autres, car le message n'était pas particulièrement évident à première vue. L'indication est que le caractère <
non valide est le premier caractère HTML de l'erreur/réponse, ce qui est probablement le résultat du type de demande non défini sur application/json
; le serveur répond par conséquent avec une réponse autre que JSON.
Au cas où cela aiderait quelqu'un,
Pour moi, le problème était que j'essayais d'analyser le JSON déjà analysé.
Et au cas où quelqu'un aurait le même problème que moi, je devais appeler JSON.stringify sur mes données de publication.