Herbie Ong 4f0be71f91 encoding/jsonpb: add unmarshal option to ignore unknown fields
This feature seems to be used quite a bit, and the conformance tests
treat this as required, perhaps as a "required option" since the
developer guide states:

"Proto3 JSON parser should reject unknown fields by default but may
provide an option to ignore unknown fields in parsing."

Also, all invalid UTF-8 errors in skipped values are also returned as it
is similar to a parse error, except it is a non-fatal one.

Change-Id: Ia26e9a355daecdbf99af23f3061353fffa32d47d
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/174017
Reviewed-by: Damien Neil <dneil@google.com>
2019-04-29 22:11:02 +00:00

127 lines
3.2 KiB
Go

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This binary implements the conformance test subprocess protocol as documented
// in conformance.proto.
package main
import (
"encoding/binary"
"io"
"log"
"os"
"github.com/golang/protobuf/v2/encoding/jsonpb"
"github.com/golang/protobuf/v2/proto"
pb "github.com/golang/protobuf/v2/internal/testprotos/conformance"
)
func main() {
var sizeBuf [4]byte
inbuf := make([]byte, 0, 4096)
for {
_, err := io.ReadFull(os.Stdin, sizeBuf[:])
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("conformance: read request: %v", err)
}
size := binary.LittleEndian.Uint32(sizeBuf[:])
if int(size) > cap(inbuf) {
inbuf = make([]byte, size)
}
inbuf = inbuf[:size]
if _, err := io.ReadFull(os.Stdin, inbuf); err != nil {
log.Fatalf("conformance: read request: %v", err)
}
req := &pb.ConformanceRequest{}
if err := proto.Unmarshal(inbuf, req); err != nil {
log.Fatalf("conformance: parse request: %v", err)
}
res := handle(req)
out, err := proto.Marshal(res)
if err != nil {
log.Fatalf("conformance: marshal response: %v", err)
}
binary.LittleEndian.PutUint32(sizeBuf[:], uint32(len(out)))
if _, err := os.Stdout.Write(sizeBuf[:]); err != nil {
log.Fatalf("conformance: write response: %v", err)
}
if _, err := os.Stdout.Write(out); err != nil {
log.Fatalf("conformance: write response: %v", err)
}
}
}
func handle(req *pb.ConformanceRequest) *pb.ConformanceResponse {
var err error
var msg proto.Message = &pb.TestAllTypesProto2{}
if req.GetMessageType() == "protobuf_test_messages.proto3.TestAllTypesProto3" {
msg = &pb.TestAllTypesProto3{}
}
switch p := req.Payload.(type) {
case *pb.ConformanceRequest_ProtobufPayload:
err = proto.Unmarshal(p.ProtobufPayload, msg)
case *pb.ConformanceRequest_JsonPayload:
err = jsonpb.UnmarshalOptions{
DiscardUnknown: req.TestCategory == pb.TestCategory_JSON_IGNORE_UNKNOWN_PARSING_TEST,
}.Unmarshal(msg, []byte(p.JsonPayload))
default:
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_RuntimeError{
RuntimeError: "unknown request payload type",
},
}
}
if err != nil {
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_ParseError{
ParseError: err.Error(),
},
}
}
switch req.RequestedOutputFormat {
case pb.WireFormat_PROTOBUF:
p, err := proto.Marshal(msg)
if err != nil {
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_SerializeError{
SerializeError: err.Error(),
},
}
}
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_ProtobufPayload{
ProtobufPayload: p,
},
}
case pb.WireFormat_JSON:
p, err := jsonpb.Marshal(msg)
if err != nil {
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_SerializeError{
SerializeError: err.Error(),
},
}
}
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_JsonPayload{
JsonPayload: string(p),
},
}
default:
return &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_RuntimeError{
RuntimeError: "unknown output format",
},
}
}
}