Skip to content

Instantly share code, notes, and snippets.

@alexedwards
Last active August 21, 2026 16:19
Show Gist options
  • Select an option

  • Save alexedwards/52913137474eb648cdfba94e2bbcbbe8 to your computer and use it in GitHub Desktop.

Select an option

Save alexedwards/52913137474eb648cdfba94e2bbcbbe8 to your computer and use it in GitHub Desktop.
func (app *application) readJSONArray(w http.ResponseWriter, r *http.Request, dst any) error {
return app.readJSON(w, r, dst, jsontext.KindBeginArray)
}
func (app *application) readJSONObject(w http.ResponseWriter, r *http.Request, dst any) error {
return app.readJSON(w, r, dst, jsontext.KindBeginObject)
}
func (app *application) readJSON(w http.ResponseWriter, r *http.Request, dst any, expectedFirstToken jsontext.Kind) error {
err := json.UnmarshalRead(r.Body, dst)
if err != nil {
var synErr *jsontext.SyntacticError
var semErr *json.SemanticError
switch {
case errors.As(err, &synErr):
return convertSyntacticErrorToBadRequest(synErr)
case errors.As(err, &semErr) && isClientSemanticError(semErr, expectedFirstToken):
return convertSemanticErrorToBadRequest(semErr)
default:
return err
}
}
return nil
}
func isClientSemanticError(semErr *json.SemanticError, expectedFirstToken jsontext.Kind) bool {
if semErr.Err != nil {
msgs := []string{
"ambiguous object member name",
"cannot derive concrete type for nil interface with finite type set",
"Go struct has no exported fields",
"value must be passed as a non-nil pointer reference",
}
if slices.Contains(msgs, semErr.Err.Error()) {
return false
}
}
if semErr.JSONKind == 0 {
return false
}
if semErr.ByteOffset == 0 && semErr.JSONKind != expectedFirstToken {
return true
}
if semErr.ByteOffset > 0 {
return true
}
return false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment