Preserve exact JSON validation semantics

This commit is contained in:
2026-08-11 22:55:17 +00:00
parent e83a3ce179
commit a93b799236
3 changed files with 347 additions and 18 deletions

View File

@@ -1,10 +1,12 @@
package validate
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"net/url"
"os"
@@ -175,18 +177,17 @@ func validateArtifact(ctx context.Context, artifact *domain.Artifact, contract d
res.IsValid = true
return res, nil
case domain.ValidationJSON:
_, jsonErr := parseJSON(artifact.Body)
if jsonErr != nil {
if !json.Valid(artifact.Body) {
res.Status = domain.ValidationFailed
res.IsValid = false
res.Errors = []string{fmt.Sprintf("invalid JSON: %v", jsonErr)}
res.Errors = []string{"invalid JSON"}
return res, nil
}
res.Status = domain.ValidationPassed
res.IsValid = true
return res, nil
case domain.ValidationJSONSchema:
instance, jsonErr := parseJSON(artifact.Body)
instance, jsonErr := decodeJSONValue(artifact.Body)
if jsonErr != nil {
res.Status = domain.ValidationFailed
res.IsValid = false
@@ -260,12 +261,22 @@ func (v *FSValidator) validateJSONSchema(instance any, schemaPath string) ([]str
return nil, nil
}
func parseJSON(body []byte) (any, error) {
var v any
if err := json.Unmarshal(body, &v); err != nil {
func decodeJSONValue(body []byte) (any, error) {
decoder := json.NewDecoder(bytes.NewReader(body))
decoder.UseNumber()
var value any
if err := decoder.Decode(&value); err != nil {
return nil, err
}
return v, nil
var trailing any
if err := decoder.Decode(&trailing); errors.Is(err, io.EOF) {
return value, nil
} else if err != nil {
return nil, err
}
return nil, errors.New("multiple JSON values")
}
func (v *StandardValidator) LoadSchemaDocument(ctx context.Context, schemaPath string) (any, error) {
@@ -285,8 +296,8 @@ func (v *StandardValidator) LoadSchemaDocument(ctx context.Context, schemaPath s
return nil, fmt.Errorf("failed to read schema file %q: %w", resolved, err)
}
var doc any
if err := json.Unmarshal(raw, &doc); err != nil {
doc, err := decodeJSONValue(raw)
if err != nil {
return nil, fmt.Errorf("failed to decode JSON schema %q: %w", resolved, err)
}
if err := validateSchemaDialect(doc); err != nil {
@@ -356,8 +367,8 @@ func (v *FSValidator) loadSchemaDocument(schemaPath string) (string, any, error)
return "", nil, fmt.Errorf("failed to read schema file %q: %w", resolved, err)
}
var doc any
if err := json.Unmarshal(raw, &doc); err != nil {
doc, err := decodeJSONValue(raw)
if err != nil {
return "", nil, fmt.Errorf("failed to decode JSON schema %q: %w", resolved, err)
}
if err := validateSchemaDialect(doc); err != nil {
@@ -490,8 +501,8 @@ func loadJSONSchemaFile(name string) (any, error) {
if err != nil {
return nil, err
}
var doc any
if err := json.Unmarshal(raw, &doc); err != nil {
doc, err := decodeJSONValue(raw)
if err != nil {
return nil, err
}
if err := validateSchemaDialect(doc); err != nil {
@@ -538,8 +549,8 @@ func (l fsSchemaLoader) Load(resourceURL string) (any, error) {
if err != nil {
return nil, err
}
var doc any
if err := json.Unmarshal(raw, &doc); err != nil {
doc, err := decodeJSONValue(raw)
if err != nil {
return nil, err
}
if err := validateSchemaDialect(doc); err != nil {