Skip to content

Commit ae23f7a

Browse files
cpuguy83thanethomson
authored andcommitted
Add support for RawMessage, similar to json.RawMessage
This adds a type that users can use to refer to raw data in the yaml for deferred decoding. Signed-off-by: Brian Goff <[email protected]>
1 parent 25e5d90 commit ae23f7a

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

yaml.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,34 @@ func RegisterCustomUnmarshalerContext[T any](unmarshaler func(context.Context, *
324324
return unmarshaler(ctx, v.(*T), b)
325325
}
326326
}
327+
328+
// RawMessage is a raw encoded YAML value. It implements [BytesMarshaler] and
329+
// [BytesUnmarshaler] and can be used to delay YAML decoding or precompute a YAML
330+
// encoding.
331+
// It also implements [json.Marshaler] and [json.Unmarshaler].
332+
//
333+
// This is similar to [json.RawMessage] in the stdlib.
334+
type RawMessage []byte
335+
336+
func (m RawMessage) MarshalYAML() ([]byte, error) {
337+
if m == nil {
338+
return []byte("null"), nil
339+
}
340+
return m, nil
341+
}
342+
343+
func (m *RawMessage) UnmarshalYAML(dt []byte) error {
344+
if m == nil {
345+
return errors.New("yaml.RawMessage: UnmarshalYAML on nil pointer")
346+
}
347+
*m = append((*m)[0:0], dt...)
348+
return nil
349+
}
350+
351+
func (m *RawMessage) UnmarshalJSON(b []byte) error {
352+
return m.UnmarshalYAML(b)
353+
}
354+
355+
func (m RawMessage) MarshalJSON() ([]byte, error) {
356+
return YAMLToJSON(m)
357+
}

yaml_test.go

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package yaml_test
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"io"
67
"reflect"
@@ -78,7 +79,7 @@ foo: bar # comment
7879
}
7980

8081
func TestDecodeKeepAddress(t *testing.T) {
81-
var data = `
82+
data := `
8283
a: &a [_]
8384
b: &b [*a,*a]
8485
c: &c [*b,*b]
@@ -103,7 +104,7 @@ d: &d [*c,*c]
103104
}
104105

105106
func TestSmartAnchor(t *testing.T) {
106-
var data = `
107+
data := `
107108
a: &a [_,_,_,_,_,_,_,_,_,_,_,_,_,_,_]
108109
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a,*a]
109110
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b,*b]
@@ -263,3 +264,91 @@ foo: 2
263264
}
264265
}
265266
}
267+
268+
func checkRawValue[T any](t *testing.T, v yaml.RawMessage, expected T) {
269+
t.Helper()
270+
271+
var actual T
272+
273+
if err := yaml.Unmarshal(v, &actual); err != nil {
274+
t.Errorf("failed to unmarshal: %v", err)
275+
return
276+
}
277+
278+
if !reflect.DeepEqual(expected, actual) {
279+
t.Errorf("expected %v, got %v", expected, actual)
280+
}
281+
}
282+
283+
func checkJSONRawValue[T any](t *testing.T, v json.RawMessage, expected T) {
284+
t.Helper()
285+
286+
var actual T
287+
288+
if err := json.Unmarshal(v, &actual); err != nil {
289+
t.Errorf("failed to unmarshal: %v", err)
290+
return
291+
}
292+
293+
if !reflect.DeepEqual(expected, actual) {
294+
t.Errorf("expected %v, got %v", expected, actual)
295+
}
296+
297+
checkRawValue(t, yaml.RawMessage(v), expected)
298+
}
299+
300+
func TestRawMessage(t *testing.T) {
301+
data := []byte(`
302+
a: 1
303+
b: "asdf"
304+
c:
305+
foo: bar
306+
`)
307+
308+
var m map[string]yaml.RawMessage
309+
if err := yaml.Unmarshal(data, &m); err != nil {
310+
t.Fatal(err)
311+
}
312+
313+
if len(m) != 3 {
314+
t.Fatalf("failed to decode: %d", len(m))
315+
}
316+
317+
checkRawValue(t, m["a"], 1)
318+
checkRawValue(t, m["b"], "asdf")
319+
checkRawValue(t, m["c"], map[string]string{"foo": "bar"})
320+
321+
dt, err := yaml.Marshal(m)
322+
if err != nil {
323+
t.Fatal(err)
324+
}
325+
var m2 map[string]yaml.RawMessage
326+
if err := yaml.Unmarshal(dt, &m2); err != nil {
327+
t.Fatal(err)
328+
}
329+
330+
checkRawValue(t, m2["a"], 1)
331+
checkRawValue(t, m2["b"], "asdf")
332+
checkRawValue(t, m2["c"], map[string]string{"foo": "bar"})
333+
334+
dt, err = json.Marshal(m2)
335+
if err != nil {
336+
t.Fatal(err)
337+
}
338+
339+
var m3 map[string]yaml.RawMessage
340+
if err := yaml.Unmarshal(dt, &m3); err != nil {
341+
t.Fatal(err)
342+
}
343+
checkRawValue(t, m3["a"], 1)
344+
checkRawValue(t, m3["b"], "asdf")
345+
checkRawValue(t, m3["c"], map[string]string{"foo": "bar"})
346+
347+
var m4 map[string]json.RawMessage
348+
if err := json.Unmarshal(dt, &m4); err != nil {
349+
t.Fatal(err)
350+
}
351+
checkJSONRawValue(t, m4["a"], 1)
352+
checkJSONRawValue(t, m4["b"], "asdf")
353+
checkJSONRawValue(t, m4["c"], map[string]string{"foo": "bar"})
354+
}

0 commit comments

Comments
 (0)