샘플
google.golang.org/protobuf v1.33.0: protojson.Marshal
검증된 샘플 — golang google.golang.org/protobuf v1.33.0: protojson.Marshal. go 1.26 · linux debian/x64 · docker에서 contract를 실행해 통과했습니다.
sha256:8c4cdd4e13ceba0e368358a62a5eadb767e490293f71c8ff3166c54b0331807f
이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다.
통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다.
MIT-0
실행 증거
선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.
- 증거 기준
- 서명된 컨트랙트 통과
- 검증 영수증
- 1
- 빌드한 서명 키
- 1
선언된 환경
go linux 24 · ubuntu · glibc 2.39 x64 go go go
검증 실행 환경
| 환경 | 컨트랙트 | 단계 | 실행일 |
|---|---|---|---|
| go 1.26 · linux debian/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · golang@1golang:1.26@sha256:e30143be198a… |
2026-09-01 |
케이스
HOW- 목표
- verify protojson.Marshal in pkg:golang/google.golang.org/protobuf@v1.33.0
- 심벌
-
- protojson.Marshal
- 환경
- go
- 생성일
- 2026-09-01T23:10:00Z
컨트랙트
- protojson.Marshal serializes protocol buffer messages to JSON format using camelCase field names by default, string representation for enums, and string representation for 64-bit integers.
- protojson.MarshalOptions.UseProtoNames outputs original protobuf snake_case field names instead of camelCase.
- protojson.MarshalOptions.EmitUnpopulated includes unpopulated fields with default zero values in the resulting JSON output.
- protojson.MarshalOptions.UseEnumNumbers outputs integer numeric values for enum fields instead of string enum names.
- protojson.Marshal serializes Google well-known types (Timestamp as RFC 3339 string, Duration as seconds string with suffix s, and Struct/Value as native JSON objects).
- protojson.MarshalOptions with Multiline and Indent produces formatted indented multiline JSON output.
파일
- NOTES.md
- PROMPT.md
- csx.json
- go.mod
- go.sum
- protojson_test.go
- spec.json
소스
# Protocol Buffers JSON Serialization with protojson.Marshal
This sample verifies JSON encoding semantics of `protojson.Marshal` in `google.golang.org/protobuf` pinned to version `v1.33.0`.
## Verified Behaviors
1. **Default JSON Mapping Rules**:
- `protojson.Marshal` converts protocol buffer message fields to JSON using camelCase field names by default.
- Enums are marshaled using their string name identifiers (e.g. `"ROLE_ADMIN"`).
- 64-bit integers (`int64`, `uint64`, `sint64`, etc.) are marshaled as strings per the proto3 JSON specification.
- Repeated fields are serialized as JSON arrays.
- Unpopulated fields are omitted by default.
2. **MarshalOptions**:
- `UseProtoNames: true`: Formats JSON keys using original protobuf snake_case field names instead of camelCase.
- `EmitUnpopulated: true`: Emits fields with default zero values (e.g. empty strings, false, 0, default enum) in the output JSON.
- `UseEnumNumbers: true`: Emits numeric integer values for enum fields rather than string names.
- `Multiline: true` and `Indent: " "`: Produces formatted indented multiline JSON output.
3. **Well-Known Types Representation**:
- `google.protobuf.Timestamp`: Serialized as an RFC 3339 formatted string (e.g. `"2026-09-01T12:00:00Z"`).
- `google.protobuf.Duration`: Serialized as a seconds string with trailing `"s"` (e.g. `"120s"`).
- `google.protobuf.Struct`: Serialized directly as a native JSON object.
Clean-room public code sample — generation instructions
Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.
A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.
Goal: verify protojson.Marshal in pkg:golang/google.golang.org/protobuf@v1.33.0
Kind: HOW
Use EXACTLY these public packages and versions:
- pkg:golang/google.golang.org/protobuf@v1.33.0
Demonstrate these symbols/APIs:
- protojson.Marshal
Rules:
- One focused purpose; the smallest project that proves the goal.
- Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
- Pin every dependency with a lockfile so resolution is reproducible.
- No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
- No personal names, emails, company names, or project identifiers of any kind.
- No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
- Keep it under 200 files and 256KB packed.
{"case":{"caseId":"case:sha256:fa6dcd2035ee260c5fa42370342b21452211a56ef0ab3231083a27d2090a4a47","contract":["protojson.Marshal serializes protocol buffer messages to JSON format using camelCase field names by default, string representation for enums, and string representation for 64-bit integers.","protojson.MarshalOptions.UseProtoNames outputs original protobuf snake_case field names instead of camelCase.","protojson.MarshalOptions.EmitUnpopulated includes unpopulated fields with default zero values in the resulting JSON output.","protojson.MarshalOptions.UseEnumNumbers outputs integer numeric values for enum fields instead of string enum names.","protojson.Marshal serializes Google well-known types (Timestamp as RFC 3339 string, Duration as seconds string with suffix s, and Struct/Value as native JSON objects).","protojson.MarshalOptions with Multiline and Indent produces formatted indented multiline JSON output."],"goal":"verify protojson.Marshal in pkg:golang/google.golang.org/protobuf@v1.33.0","kind":"HOW","packages":["pkg:golang/google.golang.org/protobuf@v1.33.0"],"schemaVersion":1,"symbols":["protojson.Marshal"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"golang","language":"go","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"go","runtime":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/google.golang.org/protobuf@v1.33.0"],"schemaVersion":1,"subject":"pkg:golang/google.golang.org/protobuf@v1.33.0","symbols":["protojson.Marshal"],"verifierAdapter":"golang@1"}
module example.com/protojson
go 1.22
require google.golang.org/protobuf v1.33.0
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
package main
import (
"encoding/json"
"strings"
"testing"
"time"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
)
func buildUserMessageDescriptor(t *testing.T) (protoreflect.MessageDescriptor, protoreflect.EnumDescriptor) {
t.Helper()
fd := &descriptorpb.FileDescriptorProto{
Name: proto.String("sample.proto"),
Syntax: proto.String("proto3"),
Package: proto.String("sample"),
MessageType: []*descriptorpb.DescriptorProto{
{
Name: proto.String("User"),
Field: []*descriptorpb.FieldDescriptorProto{
{
Name: proto.String("user_id"),
Number: proto.Int32(1),
Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
JsonName: proto.String("userId"),
},
{
Name: proto.String("display_name"),
Number: proto.Int32(2),
Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
JsonName: proto.String("displayName"),
},
{
Name: proto.String("role"),
Number: proto.Int32(3),
Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
TypeName: proto.String(".sample.Role"),
JsonName: proto.String("role"),
},
{
Name: proto.String("tags"),
Number: proto.Int32(4),
Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
JsonName: proto.String("tags"),
},
{
Name: proto.String("is_active"),
Number: proto.Int32(5),
Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
JsonName: proto.String("isActive"),
},
},
},
},
EnumType: []*descriptorpb.EnumDescriptorProto{
{
Name: proto.String("Role"),
Value: []*descriptorpb.EnumValueDescriptorProto{
{
Name: proto.String("ROLE_UNSPECIFIED"),
Number: proto.Int32(0),
},
{
Name: proto.String("ROLE_ADMIN"),
Number: proto.Int32(1),
},
{
Name: proto.String("ROLE_USER"),
Number: proto.Int32(2),
},
},
},
},
}
file, err := protodesc.NewFile(fd, nil)
if err != nil {
t.Fatalf("protodesc.NewFile failed: %v", err)
}
msgDesc := file.Messages().ByName("User")
if msgDesc == nil {
t.Fatalf("User message descriptor not found")
}
enumDesc := file.Enums().ByName("Role")
if enumDesc == nil {
t.Fatalf("Role enum descriptor not found")
}
return msgDesc, enumDesc
}
func TestProtojsonMarshalDefaultRules(t *testing.T) {
msgDesc, enumDesc := buildUserMessageDescriptor(t)
msg := dynamicpb.NewMessage(msgDesc)
msg.Set(msgDesc.Fields().ByName("user_id"), protoreflect.ValueOfInt64(42))
msg.Set(msgDesc.Fields().ByName("display_name"), protoreflect.ValueOfString("Alice"))
msg.Set(msgDesc.Fields().ByName("role"), protoreflect.ValueOfEnum(enumDesc.Values().ByName("ROLE_ADMIN").Number()))
msg.Set(msgDesc.Fields().ByName("is_active"), protoreflect.ValueOfBool(true))
tagsList := msg.Mutable(msgDesc.Fields().ByName("tags")).List()
tagsList.Append(protoreflect.ValueOfString("alpha"))
tagsList.Append(protoreflect.ValueOfString("beta"))
jsonBytes, err := protojson.Marshal(msg)
if err != nil {
t.Fatalf("protojson.Marshal failed: %v", err)
}
var parsed map[string]any
if err := json.Unmarshal(jsonBytes, &parsed); err != nil {
t.Fatalf("json.Unmarshal failed to parse protojson output: %v", err)
}
if parsed["userId"] != "42" {
t.Fatalf("expected userId string '42', got %v", parsed["userId"])
}
if parsed["displayName"] != "Alice" {
t.Fatalf("expected displayName 'Alice', got %v", parsed["displayName"])
}
if parsed["role"] != "ROLE_ADMIN" {
t.Fatalf("expected role 'ROLE_ADMIN', got %v", parsed["role"])
}
if parsed["isActive"] != true {
t.Fatalf("expected isActive true, got %v", parsed["isActive"])
}
tags, ok := parsed["tags"].([]any)
if !ok || len(tags) != 2 || tags[0] != "alpha" || tags[1] != "beta" {
t.Fatalf("expected tags ['alpha', 'beta'], got %v", parsed["tags"])
}
// Roundtrip verification with unmarshal
unmarshaled := dynamicpb.NewMessage(msgDesc)
if err := protojson.Unmarshal(jsonBytes, unmarshaled); err != nil {
t.Fatalf("protojson.Unmarshal failed: %v", err)
}
if !proto.Equal(msg, unmarshaled) {
t.Fatalf("unmarshaled message does not match original: got %v, want %v", unmarshaled, msg)
}
}
func TestProtojsonMarshalOptionsUseProtoNames(t *testing.T) {
msgDesc, _ := buildUserMessageDescriptor(t)
msg := dynamicpb.NewMessage(msgDesc)
msg.Set(msgDesc.Fields().ByName("user_id"), protoreflect.ValueOfInt64(100))
msg.Set(msgDesc.Fields().ByName("display_name"), protoreflect.ValueOfString("Bob"))
msg.Set(msgDesc.Fields().ByName("is_active"), protoreflect.ValueOfBool(true))
// Default opts uses camelCase
defaultJson, err := protojson.Marshal(msg)
if err != nil {
t.Fatalf("protojson.Marshal failed: %v", err)
}
if !strings.Contains(string(defaultJson), `"displayName"`) || !strings.Contains(string(defaultJson), `"isActive"`) {
t.Fatalf("expected camelCase in default output: %s", string(defaultJson))
}
// With UseProtoNames: true, uses snake_case proto field names
opts := protojson.MarshalOptions{
UseProtoNames: true,
}
protoNameJson, err := opts.Marshal(msg)
if err != nil {
t.Fatalf("opts.Marshal failed: %v", err)
}
if !strings.Contains(string(protoNameJson), `"display_name"`) || !strings.Contains(string(protoNameJson), `"is_active"`) {
t.Fatalf("expected proto names in output: %s", string(protoNameJson))
}
if strings.Contains(string(protoNameJson), `"displayName"`) || strings.Contains(string(protoNameJson), `"isActive"`) {
t.Fatalf("unexpected camelCase in UseProtoNames output: %s", string(protoNameJson))
}
}
func TestProtojsonMarshalOptionsEmitUnpopulated(t *testing.T) {
msgDesc, _ := buildUserMessageDescriptor(t)
// Message with only user_id set
msg := dynamicpb.NewMessage(msgDesc)
msg.Set(msgDesc.Fields().ByName("user_id"), protoreflect.ValueOfInt64(7))
// Default: unpopulated fields omitted
defaultJson, err := protojson.Marshal(msg)
if err != nil {
t.Fatalf("protojson.Marshal failed: %v", err)
}
if strings.Contains(string(defaultJson), `"displayName"`) || strings.Contains(string(defaultJson), `"isActive"`) {
t.Fatalf("default protojson.Marshal should omit unpopulated fields: %s", string(defaultJson))
}
// EmitUnpopulated: true emits zero values for unpopulated fields
opts := protojson.MarshalOptions{
EmitUnpopulated: true,
}
unpopulatedJson, err := opts.Marshal(msg)
if err != nil {
t.Fatalf("opts.Marshal with EmitUnpopulated failed: %v", err)
}
var parsed map[string]any
if err := json.Unmarshal(unpopulatedJson, &parsed); err != nil {
t.Fatalf("failed to parse JSON with EmitUnpopulated: %v", err)
}
if parsed["displayName"] != "" {
t.Fatalf("expected displayName to be empty string, got %v", parsed["displayName"])
}
if parsed["isActive"] != false {
t.Fatalf("expected isActive to be false, got %v", parsed["isActive"])
}
if parsed["role"] != "ROLE_UNSPECIFIED" {
t.Fatalf("expected role to be default enum 'ROLE_UNSPECIFIED', got %v", parsed["role"])
}
}
func TestProtojsonMarshalOptionsUseEnumNumbers(t *testing.T) {
msgDesc, enumDesc := buildUserMessageDescriptor(t)
msg := dynamicpb.NewMessage(msgDesc)
msg.Set(msgDesc.Fields().ByName("role"), protoreflect.ValueOfEnum(enumDesc.Values().ByName("ROLE_ADMIN").Number()))
opts := protojson.MarshalOptions{
UseEnumNumbers: true,
}
enumNumJson, err := opts.Marshal(msg)
if err != nil {
t.Fatalf("opts.Marshal with UseEnumNumbers failed: %v", err)
}
var parsed map[string]any
if err := json.Unmarshal(enumNumJson, &parsed); err != nil {
t.Fatalf("failed to parse enum number JSON: %v", err)
}
// ROLE_ADMIN has number 1
if parsed["role"] != float64(1) {
t.Fatalf("expected role to be integer 1, got %v", parsed["role"])
}
}
func TestProtojsonMarshalWellKnownTypes(t *testing.T) {
// Timestamp formatting as RFC 3339 string
ts := timestamppb.New(time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC))
tsJson, err := protojson.Marshal(ts)
if err != nil {
t.Fatalf("protojson.Marshal timestamppb failed: %v", err)
}
expectedTs := `"2026-09-01T12:00:00Z"`
if string(tsJson) != expectedTs {
t.Fatalf("expected timestamp JSON %s, got %s", expectedTs, string(tsJson))
}
// Duration formatting as seconds string with suffix 's'
dur := durationpb.New(120 * time.Second)
durJson, err := protojson.Marshal(dur)
if err != nil {
t.Fatalf("protojson.Marshal durationpb failed: %v", err)
}
if string(durJson) != `"120s"` {
t.Fatalf("expected duration JSON '\"120s\"', got %s", string(durJson))
}
// Structpb / Value dynamic json object representation
st, err := structpb.NewStruct(map[string]any{
"key": "value",
"number": 123.45,
"flag": true,
})
if err != nil {
t.Fatalf("structpb.NewStruct failed: %v", err)
}
stJson, err := protojson.Marshal(st)
if err != nil {
t.Fatalf("protojson.Marshal structpb failed: %v", err)
}
var stMap map[string]any
if err := json.Unmarshal(stJson, &stMap); err != nil {
t.Fatalf("failed to parse structpb JSON: %v", err)
}
if stMap["key"] != "value" || stMap["number"] != 123.45 || stMap["flag"] != true {
t.Fatalf("unexpected structpb JSON content: %v", stMap)
}
}
func TestProtojsonMarshalOptionsMultilineIndent(t *testing.T) {
st, _ := structpb.NewStruct(map[string]any{"service": "auth"})
opts := protojson.MarshalOptions{
Multiline: true,
Indent: " ",
}
multiJson, err := opts.Marshal(st)
if err != nil {
t.Fatalf("opts.Marshal multiline failed: %v", err)
}
jsonStr := string(multiJson)
if !strings.Contains(jsonStr, "\n") || !strings.Contains(jsonStr, " ") {
t.Fatalf("expected multiline indented output: %s", jsonStr)
}
}
{
"schemaVersion": 1,
"goal": "verify protojson.Marshal in pkg:golang/google.golang.org/protobuf@v1.33.0",
"kind": "HOW",
"packages": [
"pkg:golang/google.golang.org/protobuf@v1.33.0"
],
"symbols": [
"protojson.Marshal"
]
}
오리진 시더
익명