@@ -10,6 +10,7 @@ import (
10
10
"encoding"
11
11
"errors"
12
12
"fmt"
13
+ "internal/godebug"
13
14
"io"
14
15
"reflect"
15
16
"strconv"
@@ -77,6 +78,15 @@ const (
77
78
// See [MarshalIndent] for an example.
78
79
//
79
80
// Marshal will return an error if asked to marshal a channel, function, or map.
81
+ //
82
+ // Before Go 1.24, the marshaling was inconsistent: custom marshalers
83
+ // (MarshalXML, MarshalXMLAttr, MarshalText methods) defined with pointer receivers
84
+ // were not called for non-addressable values. Also, MarshalXMLAttr and MarshalText
85
+ // were not called for struct fields marked as attribute/CDATA/chardata having interface types.
86
+ // As of Go 1.24, the marshaling is consistent.
87
+ //
88
+ // The GODEBUG setting xmlinconsistentmarshal=1 restores pre-Go 1.24
89
+ // inconsistent marshaling.
80
90
func Marshal (v any ) ([]byte , error ) {
81
91
var b bytes.Buffer
82
92
enc := NewEncoder (& b )
@@ -167,7 +177,7 @@ func (enc *Encoder) Indent(prefix, indent string) {
167
177
//
168
178
// Encode calls [Encoder.Flush] before returning.
169
179
func (enc * Encoder ) Encode (v any ) error {
170
- err := enc .p .marshalValue (reflect .ValueOf (v ), nil , nil )
180
+ err := enc .p .marshalValue (reflect .ValueOf (v ), nil , nil , false )
171
181
if err != nil {
172
182
return err
173
183
}
@@ -182,7 +192,7 @@ func (enc *Encoder) Encode(v any) error {
182
192
//
183
193
// EncodeElement calls [Encoder.Flush] before returning.
184
194
func (enc * Encoder ) EncodeElement (v any , start StartElement ) error {
185
- err := enc .p .marshalValue (reflect .ValueOf (v ), nil , & start )
195
+ err := enc .p .marshalValue (reflect .ValueOf (v ), nil , & start , false )
186
196
if err != nil {
187
197
return err
188
198
}
@@ -415,14 +425,15 @@ func (p *printer) popPrefix() {
415
425
}
416
426
417
427
var (
418
- marshalerType = reflect .TypeFor [Marshaler ]()
419
- marshalerAttrType = reflect .TypeFor [MarshalerAttr ]()
420
- textMarshalerType = reflect .TypeFor [encoding.TextMarshaler ]()
428
+ marshalerType = reflect .TypeFor [Marshaler ]()
429
+ marshalerAttrType = reflect .TypeFor [MarshalerAttr ]()
430
+ textMarshalerType = reflect .TypeFor [encoding.TextMarshaler ]()
431
+ xmlinconsistentmarshal = godebug .New ("xmlinconsistentmarshal" )
421
432
)
422
433
423
434
// marshalValue writes one or more XML elements representing val.
424
435
// If val was obtained from a struct field, finfo must have its details.
425
- func (p * printer ) marshalValue (val reflect.Value , finfo * fieldInfo , startTemplate * StartElement ) error {
436
+ func (p * printer ) marshalValue (val reflect.Value , finfo * fieldInfo , startTemplate * StartElement , incrementedOldBehaviorCounter bool ) error {
426
437
if startTemplate != nil && startTemplate .Name .Local == "" {
427
438
return fmt .Errorf ("xml: EncodeElement of StartElement with missing name" )
428
439
}
@@ -452,30 +463,36 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
452
463
return p .marshalInterface (val .Interface ().(Marshaler ), defaultStart (typ , finfo , startTemplate ))
453
464
}
454
465
455
- var pv reflect.Value
456
- if val .CanAddr () {
457
- pv = val .Addr ()
458
- } else {
459
- pv = reflect .New (typ )
460
- pv .Elem ().Set (val )
461
- }
462
-
463
- if pv .CanInterface () && pv .Type ().Implements (marshalerType ) {
464
- return p .marshalInterface (pv .Interface ().(Marshaler ), defaultStart (pv .Type (), finfo , startTemplate ))
466
+ if val .CanInterface () && reflect .PointerTo (typ ).Implements (marshalerType ) {
467
+ pv := addrOrNew (val )
468
+ if pv .IsValid () {
469
+ return p .marshalInterface (pv .Interface ().(Marshaler ), defaultStart (pv .Type (), finfo , startTemplate ))
470
+ }
471
+ if ! incrementedOldBehaviorCounter {
472
+ xmlinconsistentmarshal .IncNonDefault ()
473
+ incrementedOldBehaviorCounter = true
474
+ }
465
475
}
466
476
467
477
// Check for text marshaler.
468
478
if val .CanInterface () && typ .Implements (textMarshalerType ) {
469
479
return p .marshalTextInterface (val .Interface ().(encoding.TextMarshaler ), defaultStart (typ , finfo , startTemplate ))
470
480
}
471
- if pv .CanInterface () && pv .Type ().Implements (textMarshalerType ) {
472
- return p .marshalTextInterface (pv .Interface ().(encoding.TextMarshaler ), defaultStart (pv .Type (), finfo , startTemplate ))
481
+ if val .CanInterface () && reflect .PointerTo (typ ).Implements (textMarshalerType ) {
482
+ pv := addrOrNew (val )
483
+ if pv .IsValid () {
484
+ return p .marshalTextInterface (pv .Interface ().(encoding.TextMarshaler ), defaultStart (pv .Type (), finfo , startTemplate ))
485
+ }
486
+ if ! incrementedOldBehaviorCounter {
487
+ xmlinconsistentmarshal .IncNonDefault ()
488
+ incrementedOldBehaviorCounter = true
489
+ }
473
490
}
474
491
475
492
// Slices and arrays iterate over the elements. They do not have an enclosing tag.
476
493
if (kind == reflect .Slice || kind == reflect .Array ) && typ .Elem ().Kind () != reflect .Uint8 {
477
494
for i , n := 0 , val .Len (); i < n ; i ++ {
478
- if err := p .marshalValue (val .Index (i ), finfo , startTemplate ); err != nil {
495
+ if err := p .marshalValue (val .Index (i ), finfo , startTemplate , incrementedOldBehaviorCounter ); err != nil {
479
496
return err
480
497
}
481
498
}
@@ -526,6 +543,8 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
526
543
527
544
// Attributes
528
545
for i := range tinfo .fields {
546
+ attrIncrementedOldBehaviorCounter := incrementedOldBehaviorCounter
547
+
529
548
finfo := & tinfo .fields [i ]
530
549
if finfo .flags & fAttr == 0 {
531
550
continue
@@ -540,8 +559,17 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
540
559
continue
541
560
}
542
561
562
+ if fv .Kind () == reflect .Interface {
563
+ if xmlinconsistentmarshal .Value () != "1" {
564
+ fv = fv .Elem ()
565
+ } else if ! attrIncrementedOldBehaviorCounter {
566
+ xmlinconsistentmarshal .IncNonDefault ()
567
+ attrIncrementedOldBehaviorCounter = true
568
+ }
569
+ }
570
+
543
571
name := Name {Space : finfo .xmlns , Local : finfo .name }
544
- if err := p .marshalAttr (& start , name , fv ); err != nil {
572
+ if err := p .marshalAttr (& start , name , fv , attrIncrementedOldBehaviorCounter ); err != nil {
545
573
return err
546
574
}
547
575
}
@@ -557,7 +585,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
557
585
}
558
586
559
587
if val .Kind () == reflect .Struct {
560
- err = p .marshalStruct (tinfo , val )
588
+ err = p .marshalStruct (tinfo , val , incrementedOldBehaviorCounter )
561
589
} else {
562
590
s , b , err1 := p .marshalSimple (typ , val )
563
591
if err1 != nil {
@@ -580,7 +608,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
580
608
}
581
609
582
610
// marshalAttr marshals an attribute with the given name and value, adding to start.Attr.
583
- func (p * printer ) marshalAttr (start * StartElement , name Name , val reflect.Value ) error {
611
+ func (p * printer ) marshalAttr (start * StartElement , name Name , val reflect.Value , incrementedOldBehaviorCount bool ) error {
584
612
if val .CanInterface () && val .Type ().Implements (marshalerAttrType ) {
585
613
attr , err := val .Interface ().(MarshalerAttr ).MarshalXMLAttr (name )
586
614
if err != nil {
@@ -592,23 +620,22 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value)
592
620
return nil
593
621
}
594
622
595
- var pv reflect.Value
596
- if val .CanAddr () {
597
- pv = val .Addr ()
598
- } else {
599
- pv = reflect .New (val .Type ())
600
- pv .Elem ().Set (val )
601
- }
602
-
603
- if pv .CanInterface () && pv .Type ().Implements (marshalerAttrType ) {
604
- attr , err := pv .Interface ().(MarshalerAttr ).MarshalXMLAttr (name )
605
- if err != nil {
606
- return err
623
+ if val .CanInterface () && reflect .PointerTo (val .Type ()).Implements (marshalerAttrType ) {
624
+ pv := addrOrNew (val )
625
+ if pv .IsValid () {
626
+ attr , err := pv .Interface ().(MarshalerAttr ).MarshalXMLAttr (name )
627
+ if err != nil {
628
+ return err
629
+ }
630
+ if attr .Name .Local != "" {
631
+ start .Attr = append (start .Attr , attr )
632
+ }
633
+ return nil
607
634
}
608
- if attr .Name .Local != "" {
609
- start .Attr = append (start .Attr , attr )
635
+ if ! incrementedOldBehaviorCount {
636
+ xmlinconsistentmarshal .IncNonDefault ()
637
+ incrementedOldBehaviorCount = true
610
638
}
611
- return nil
612
639
}
613
640
614
641
if val .CanInterface () && val .Type ().Implements (textMarshalerType ) {
@@ -620,13 +647,20 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value)
620
647
return nil
621
648
}
622
649
623
- if pv .CanInterface () && pv .Type ().Implements (textMarshalerType ) {
624
- text , err := pv .Interface ().(encoding.TextMarshaler ).MarshalText ()
625
- if err != nil {
626
- return err
650
+ if val .CanInterface () && reflect .PointerTo (val .Type ()).Implements (textMarshalerType ) {
651
+ pv := addrOrNew (val )
652
+ if pv .IsValid () {
653
+ text , err := pv .Interface ().(encoding.TextMarshaler ).MarshalText ()
654
+ if err != nil {
655
+ return err
656
+ }
657
+ start .Attr = append (start .Attr , Attr {name , string (text )})
658
+ return nil
659
+ }
660
+ if ! incrementedOldBehaviorCount {
661
+ xmlinconsistentmarshal .IncNonDefault ()
662
+ incrementedOldBehaviorCount = true
627
663
}
628
- start .Attr = append (start .Attr , Attr {name , string (text )})
629
- return nil
630
664
}
631
665
632
666
// Dereference or skip nil pointer, interface values.
@@ -642,7 +676,7 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value)
642
676
if val .Kind () == reflect .Slice && val .Type ().Elem ().Kind () != reflect .Uint8 {
643
677
n := val .Len ()
644
678
for i := 0 ; i < n ; i ++ {
645
- if err := p .marshalAttr (start , name , val .Index (i )); err != nil {
679
+ if err := p .marshalAttr (start , name , val .Index (i ), incrementedOldBehaviorCount ); err != nil {
646
680
return err
647
681
}
648
682
}
@@ -834,9 +868,10 @@ func indirect(vf reflect.Value) reflect.Value {
834
868
return vf
835
869
}
836
870
837
- func (p * printer ) marshalStruct (tinfo * typeInfo , val reflect.Value ) error {
871
+ func (p * printer ) marshalStruct (tinfo * typeInfo , val reflect.Value , incrementedOldBehaviorCount bool ) error {
838
872
s := parentStack {p : p }
839
873
for i := range tinfo .fields {
874
+ fieldIncrementedOldBehaviorCount := incrementedOldBehaviorCount
840
875
finfo := & tinfo .fields [i ]
841
876
if finfo .flags & fAttr != 0 {
842
877
continue
@@ -857,6 +892,18 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
857
892
if err := s .trim (finfo .parents ); err != nil {
858
893
return err
859
894
}
895
+
896
+ if vf .Kind () == reflect .Interface && ! vf .IsNil () {
897
+ if xmlinconsistentmarshal .Value () != "1" {
898
+ vf = vf .Elem ()
899
+ } else {
900
+ if ! fieldIncrementedOldBehaviorCount {
901
+ xmlinconsistentmarshal .IncNonDefault ()
902
+ fieldIncrementedOldBehaviorCount = true
903
+ }
904
+ }
905
+ }
906
+
860
907
if vf .CanInterface () && vf .Type ().Implements (textMarshalerType ) {
861
908
data , err := vf .Interface ().(encoding.TextMarshaler ).MarshalText ()
862
909
if err != nil {
@@ -867,9 +914,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
867
914
}
868
915
continue
869
916
}
870
- if vf .CanAddr ( ) {
871
- pv := vf . Addr ( )
872
- if pv .CanInterface () && pv . Type (). Implements ( textMarshalerType ) {
917
+ if vf .CanInterface () && reflect . PointerTo ( vf . Type ()). Implements ( textMarshalerType ) {
918
+ pv := addrOrNew ( vf )
919
+ if pv .IsValid ( ) {
873
920
data , err := pv .Interface ().(encoding.TextMarshaler ).MarshalText ()
874
921
if err != nil {
875
922
return err
@@ -879,6 +926,10 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
879
926
}
880
927
continue
881
928
}
929
+ if ! fieldIncrementedOldBehaviorCount {
930
+ xmlinconsistentmarshal .IncNonDefault ()
931
+ fieldIncrementedOldBehaviorCount = true
932
+ }
882
933
}
883
934
884
935
var scratch [64 ]byte
@@ -981,7 +1032,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
981
1032
}
982
1033
}
983
1034
}
984
- if err := p .marshalValue (vf , finfo , nil ); err != nil {
1035
+ if err := p .marshalValue (vf , finfo , nil , fieldIncrementedOldBehaviorCount ); err != nil {
985
1036
return err
986
1037
}
987
1038
}
@@ -1134,3 +1185,15 @@ func isEmptyValue(v reflect.Value) bool {
1134
1185
}
1135
1186
return false
1136
1187
}
1188
+
1189
+ func addrOrNew (v reflect.Value ) reflect.Value {
1190
+ if v .CanAddr () {
1191
+ return v .Addr ()
1192
+ }
1193
+ if xmlinconsistentmarshal .Value () != "1" {
1194
+ pv := reflect .New (v .Type ())
1195
+ pv .Elem ().Set (v )
1196
+ return pv
1197
+ }
1198
+ return reflect.Value {}
1199
+ }
0 commit comments