diff --git a/helpers.go b/helpers.go index 1e4eac1..c692b1e 100644 --- a/helpers.go +++ b/helpers.go @@ -198,3 +198,15 @@ func typeNameAddrPtr(v any) string { } return rt.Name() } + +func concatBytes(s ...[]byte) []byte { + n := 0 + for _, v := range s { + n += len(v) + } + res := make([]byte, 0, n) + for _, v := range s { + res = append(res, v...) + } + return res +} diff --git a/invoice.go b/invoice.go index c13d805..2f7edbc 100644 --- a/invoice.go +++ b/invoice.go @@ -215,14 +215,14 @@ func (iv Invoice) MarshalXML(e *xml.Encoder, start xml.StartElement) error { // XML returns the XML encoding of the Invoice func (iv Invoice) XML() ([]byte, error) { - return MarshalXML(iv) + return MarshalXMLWithHeader(iv) } // XMLIndent works like XML, but each XML element begins on a new // indented line that starts with prefix and is followed by one or more // copies of indent according to the nesting depth. func (iv Invoice) XMLIndent(prefix, indent string) ([]byte, error) { - return MarshalIndentXML(iv, prefix, indent) + return MarshalIndentXMLWithHeader(iv, prefix, indent) } // UnmarshalInvoice unmarshals an Invoice from XML data. Only use this method diff --git a/xml_types.go b/xml_types.go index d678a29..066ed0d 100644 --- a/xml_types.go +++ b/xml_types.go @@ -191,18 +191,39 @@ func NewIDNode(id string) *IDNode { // MarshalXML returns the XML encoding of v in Canonical XML form [XML-C14N]. // This method must be used for marshaling objects from this library, instead -// of encoding/xml. +// of encoding/xml. This method does NOT include the XML header declaration. func MarshalXML(v any) ([]byte, error) { return xml.Marshal(v) } +// MarshalXMLWithHeader same as MarshalXML, but also add the XML header +// declaration. +func MarshalXMLWithHeader(v any) ([]byte, error) { + data, err := MarshalXML(v) + if err != nil { + return nil, err + } + return concatBytes([]byte(xml.Header), data), nil +} + // MarshalIndentXML works like MarshalXML, but each XML element begins on a new // indented line that starts with prefix and is followed by one or more -// copies of indent according to the nesting depth. +// copies of indent according to the nesting depth. This method does NOT +// include the XML header declaration. func MarshalIndentXML(v any, prefix, indent string) ([]byte, error) { return xml.MarshalIndent(v, prefix, indent) } +// MarshalIndentXMLWithHeader same as MarshalIndentXML, but also add the XML +// header declaration. +func MarshalIndentXMLWithHeader(v any, prefix, indent string) ([]byte, error) { + data, err := MarshalIndentXML(v, prefix, indent) + if err != nil { + return nil, err + } + return concatBytes([]byte(xml.Header), data), nil +} + // Unmarshal parses the XML-encoded data and stores the result in // the value pointed to by v, which must be an arbitrary struct, // slice, or string. Well-formed data that does not fit into v is