Skip to content

Commit

Permalink
Merge pull request #230 from invopop/c14n-marshal
Browse files Browse the repository at this point in the history
Support c14n direct marshalling
  • Loading branch information
samlown authored Jan 12, 2024
2 parents 2afef07 + acb9ed8 commit 43ce264
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 4 deletions.
6 changes: 3 additions & 3 deletions c14n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ One of the objectives of GoBL is to create a document that could potentially be

This `c14n` package, inspired by the works of others, thus aims to define a simple standardized approach to canonical JSON that could potentially be implemented easily in other languages. More than just a definition, the code here is a reference implementation from which libraries can be made in languages other than Go.

## GoBL JSON C14n
## GOBL JSON C14n

GoBL considers the following JSON values as explicit types:
GOBL considers the following JSON values as explicit types:

- a string
- a number, which extends the JSON spec and is split into:
Expand Down Expand Up @@ -56,7 +56,7 @@ JSON in canonical form:
2. using six-character `\u00XX` uppercase hexadecimal escape sequences for control characters that require escaping but lack a two-character sequence described previously, and
3. reject any string containing invalid encoding.

The GoBL JSON c14n package has been designed to operate using any raw JSON source and uses the Go [`encoding/json`](https://golang.org/pkg/encoding/json/) library's streaming methods to parse and recreate a document in memory. A simplified object model is used to map JSON structures ready to be converted into canonical JSON.
The GOBL JSON c14n package has been designed to operate using any raw JSON source and uses the Go [`encoding/json`](https://golang.org/pkg/encoding/json/) library's streaming methods to parse and recreate a document in memory. A simplified object model is used to map JSON structures ready to be converted into canonical JSON.

## Usage Example

Expand Down
13 changes: 13 additions & 0 deletions c14n/c14n.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
package c14n

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
)

Expand All @@ -23,6 +25,17 @@ func UnmarshalJSON(src io.Reader) (Canonicalable, error) {
return res, nil
}

// MarshalJSON takes any Go object that can be serialized into JSON and generates
// the canonical JSON representation of that object.
func MarshalJSON(src any) ([]byte, error) {
data := new(bytes.Buffer)
enc := json.NewEncoder(data)
if err := enc.Encode(src); err != nil {
return nil, fmt.Errorf("encoding: %w", err)
}
return CanonicalJSON(data)
}

// CanonicalJSON performs the unmarshal and marshal commands in one go.
func CanonicalJSON(src io.Reader) ([]byte, error) {
obj, err := UnmarshalJSON(src)
Expand Down
17 changes: 17 additions & 0 deletions c14n/c14n_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/invopop/gobl/c14n"
"github.com/stretchr/testify/assert"
)

func TestJSONToArray(t *testing.T) {
Expand Down Expand Up @@ -69,3 +70,19 @@ func TestJSONToArray(t *testing.T) {
t.Errorf("unexpected sum, please check marshaled data, got: %v", s)
}
}

func TestMarshalJSON(t *testing.T) {
obj := struct {
Title string `json:"title"`
Idx int64 `json:"idx"`
Body string `json:"body,omitempty"`
}{
Title: "test",
Idx: 1,
Body: "Test body to play around with",
}
d, err := c14n.MarshalJSON(obj)
assert.NoError(t, err)
out := `{"body":"Test body to play around with","idx":1,"title":"test"}`
assert.Contains(t, string(d), out)
}
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
type Version string

// VERSION is the current version of the GOBL library.
const VERSION Version = "v0.65.1"
const VERSION Version = "v0.65.2"

// Semver parses and returns semver
func (v Version) Semver() *semver.Version {
Expand Down

0 comments on commit 43ce264

Please sign in to comment.