Skip to content

Commit

Permalink
update readme text, examples, and badges (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
avivpxi authored Jan 29, 2023
1 parent cc4ddda commit 8d6a01c
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 71 deletions.
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

![Marshmallow Campfire](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/campfire.png)

[![CodeQL Status](https://img.shields.io/github/workflow/status/perimeterx/marshmallow/CodeQL?label=CodeQL&logo=github)](https://github.com/PerimeterX/marshmallow/actions/workflows/codeql.yml?query=branch%3Amain++)
[![Run Tests](https://img.shields.io/github/workflow/status/perimeterx/marshmallow/Go?label=Run%20Tests&logo=github)](https://github.com/PerimeterX/marshmallow/actions/workflows/go.yml?query=branch%3Amain)
[![Dependency Review](https://img.shields.io/github/workflow/status/perimeterx/marshmallow/Dependency%20Review?label=Dependency%20Review&logo=github)](https://github.com/PerimeterX/marshmallow/actions/workflows/dependency-review.yml?query=branch%3Amain)
[![CodeQL Status](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/codeql.yml?branch=main&logo=github&label=CodeQL)](https://github.com/PerimeterX/marshmallow/actions/workflows/codeql.yml?query=branch%3Amain++)
[![Run Tests](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/go.yml?branch=main&logo=github&label=Run%20Tests)](https://github.com/PerimeterX/marshmallow/actions/workflows/go.yml?query=branch%3Amain)
[![Dependency Review](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/dependency-review.yml?logo=github&label=Dependency%20Review)](https://github.com/PerimeterX/marshmallow/actions/workflows/dependency-review.yml?query=branch%3Amain)
[![Go Report Card](https://goreportcard.com/badge/github.com/perimeterx/marshmallow)](https://goreportcard.com/report/github.com/perimeterx/marshmallow)
![Manual Code Coverage](https://img.shields.io/badge/coverage-91.9%25-green)
![Manual Code Coverage](https://img.shields.io/badge/coverage-92.4%25-green)
[![Go Reference](https://pkg.go.dev/badge/github.com/perimeterx/marshmallow.svg)](https://pkg.go.dev/github.com/perimeterx/marshmallow)
[![Licence](https://img.shields.io/github/license/perimeterx/marshmallow)](LICENSE)
[![Latest Release](https://img.shields.io/github/v/release/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/releases)
Expand Down Expand Up @@ -49,7 +49,6 @@ import (
)

func main() {
marshmallow.EnableCache() // this is used to boost performance, read more below
v := struct {
Foo string `json:"foo"`
Boo []int `json:"boo"`
Expand All @@ -60,6 +59,8 @@ func main() {
}
```

**Examples can be found [here](example_test.go)**

## Performance Benchmark And Alternatives

Marshmallow performs best when dealing with mixed data - when some fields are known and some are unknown.
Expand Down Expand Up @@ -183,8 +184,6 @@ Marshmallow also supports caching of refection information using
and
[EnableCustomCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L35).

**Examples can be found [here](example_test.go)**

# Marshmallow Logo

Marshmallow logo and assets by [Adva Rom](https://www.linkedin.com/in/adva-rom-7a6738127/) are licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />
Expand Down
225 changes: 161 additions & 64 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,122 +3,235 @@ package marshmallow_test
import (
"fmt"
"github.com/perimeterx/marshmallow"
"sync"
)

type flatStruct struct {
Foo string `json:"foo"`
Boo []int `json:"boo"`
// ExampleBasicUnmarshal shows a basic example usage of marshmallow's unmarshalling capabilities
func ExampleBasicUnmarshal() {
// we have the following data
data := []byte(`{"name":"some name", "values": [1, 2, 3], "more_data": "some stuff I am not interested in..."}`)
// we want to read the "name" field, and modify the "values" field.
// we're not directly interested in the other fields, but we do want to retain them.

type exampleStruct struct {
Name string `json:"name"`
Values []int `json:"values"`
}

v := exampleStruct{}
// we pass in a struct containing the fields we're interested in,
// marshmallow will populate it and return a result map containing all data
result, err := marshmallow.Unmarshal(data, &v)
if err != nil {
panic(err)
}

fmt.Printf("Struct data: %+v\n", v)
fmt.Printf("Map data: %+v\n", result)

// now we can work with the struct data in a safe and maintainable manner
v.Values[0] = 42
fmt.Printf("Name is %s\n", v.Name)

// pointer value changes in the struct will also be visible from the map
// more info at https://github.com/PerimeterX/marshmallow/issues/23#issuecomment-1403409592
fmt.Printf("Map data after changes: %+v\n", result)

// Output:
// Struct data: {Name:some name Values:[1 2 3]}
// Map data: map[more_data:some stuff I am not interested in... name:some name values:[1 2 3]]
// Name is some name
// Map data after changes: map[more_data:some stuff I am not interested in... name:some name values:[42 2 3]]
}

// ExampleExcludeKnownFields shows how to use the WithExcludeKnownFieldsFromMap option to exclude struct fields
// from the result map. more info at https://github.com/PerimeterX/marshmallow/issues/16
func ExampleExcludeKnownFields() {
type exampleStruct struct {
Foo string `json:"foo"`
Boo []int `json:"boo"`
}

// unmarshal with mode marshmallow.ModeExcludeKnownFieldsFromReturnedMap
// this will return unmarshalled result without known fields
v := exampleStruct{}
result, err := marshmallow.Unmarshal(
[]byte(`{"foo":"bar","boo":[1,2,3],"goo":"untyped"}`),
&v,
marshmallow.WithExcludeKnownFieldsFromMap(true),
)
fmt.Printf("v=%+v, result=%+v, err=%T\n", v, result, err)
// Output:
// v={Foo:bar Boo:[1 2 3]}, result=map[goo:untyped], err=<nil>
}

// ExampleJSONDataHandler shows how to capture nested unknown fields
// more info at https://github.com/PerimeterX/marshmallow/issues/15
func ExampleJSONDataHandler() {
type parentStruct struct {
Known string `json:"known"`
Nested childStruct `json:"nested"`
}

data := []byte(`{"known": "foo","unknown": "boo","nested": {"known": "goo","unknown": "doo"}}`)
p := &parentStruct{}
_, err := marshmallow.Unmarshal(data, p)
fmt.Printf("err: %v\n", err)
fmt.Printf("nested data: %+v\n", p.Nested.Data)
// Output:
// err: <nil>
// nested data: map[known:goo unknown:doo]
}

func ExampleUnmarshal() {
// enable marshmallow cache to boost up performance by reusing field type information.
// ExampleCache shows how to enable marshmallow cache to boost up performance by reusing field type information.
// more info: https://github.com/PerimeterX/marshmallow/blob/22e3c7fe4423d7c5f317d95f84de524253e0aed3/cache.go#L35
func ExampleCache() {
// enable default cache
marshmallow.EnableCache()

type exampleStruct struct {
Foo string `json:"foo"`
Boo []int `json:"boo"`
}
v := exampleStruct{}
_, _ = marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3]}`), &v)

// enable custom cache, you can pass any implementation of the marshmallow.Cache interface
// this lets you control the size of the cache, eviction policy, or any other aspect of it.
marshmallow.EnableCustomCache(&sync.Map{})
}

// ExampleUnmarshalErrorHandling shows all error handling capabilities of marshmallow.Unmarshal
func ExampleUnmarshalErrorHandling() {
type exampleStruct struct {
Foo string `json:"foo"`
Boo []int `json:"boo"`
}

// unmarshal with mode marshmallow.ModeFailOnFirstError and valid value
// this will finish unmarshalling and return a nil err
v := flatStruct{}
v := exampleStruct{}
result, err := marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3]}`), &v)
fmt.Printf("ModeFailOnFirstError and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// unmarshal with mode marshmallow.ModeFailOnFirstError and invalid value
// this will return nil result and an error
v = flatStruct{}
v = exampleStruct{}
result, err = marshmallow.Unmarshal([]byte(`{"foo":2,"boo":[1,2,3]}`), &v)
fmt.Printf("ModeFailOnFirstError and invalid value: result=%+v, err=%T\n", result, err)

// unmarshal with mode marshmallow.ModeAllowMultipleErrors and valid value
// this will finish unmarshalling and return a nil err
v = flatStruct{}
result, err = marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3]}`), &v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors))
v = exampleStruct{}
result, err = marshmallow.Unmarshal(
[]byte(`{"foo":"bar","boo":[1,2,3]}`),
&v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors),
)
fmt.Printf("ModeAllowMultipleErrors and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// unmarshal with mode marshmallow.ModeAllowMultipleErrors and invalid value
// this will return a partially populated result and an error
v = flatStruct{}
result, err = marshmallow.Unmarshal([]byte(`{"foo":2,"boo":[1,2,3]}`), &v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors))
v = exampleStruct{}
result, err = marshmallow.Unmarshal(
[]byte(`{"foo":2,"boo":[1,2,3]}`),
&v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors),
)
fmt.Printf("ModeAllowMultipleErrors and invalid value: result=%+v, err=%T\n", result, err)

// unmarshal with mode marshmallow.ModeFailOverToOriginalValue and valid value
// this will finish unmarshalling and return a nil err
v = flatStruct{}
result, err = marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3]}`), &v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue))
v = exampleStruct{}
result, err = marshmallow.Unmarshal(
[]byte(`{"foo":"bar","boo":[1,2,3]}`),
&v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue),
)
fmt.Printf("ModeFailOverToOriginalValue and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// unmarshal with mode marshmallow.ModeFailOverToOriginalValue and invalid value
// this will return a fully unmarshalled result, failing to the original invalid values, and an error
v = flatStruct{}
result, err = marshmallow.Unmarshal([]byte(`{"foo":2,"boo":[1,2,3]}`), &v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue))
v = exampleStruct{}
result, err = marshmallow.Unmarshal(
[]byte(`{"foo":2,"boo":[1,2,3]}`),
&v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue),
)
fmt.Printf("ModeFailOverToOriginalValue and invalid value: result=%+v, err=%T\n", result, err)

// unmarshal with mode marshmallow.ModeExcludeKnownFieldsFromReturnedMap and valid value
// this will return unmarshalled result without known fields
v = flatStruct{}
result, err = marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3],"goo":"untyped"}`), &v,
marshmallow.WithExcludeKnownFieldsFromMap(true))
fmt.Printf("ModeExcludeKnownFieldsFromReturnedMap and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// Output:
// ModeFailOnFirstError and valid value: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar], err=<nil>
// ModeFailOnFirstError and invalid value: result=map[], err=*jlexer.LexerError
// ModeAllowMultipleErrors and valid value: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar], err=<nil>
// ModeAllowMultipleErrors and invalid value: result=map[boo:[1 2 3]], err=*marshmallow.MultipleLexerError
// ModeFailOverToOriginalValue and valid value: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar], err=<nil>
// ModeFailOverToOriginalValue and invalid value: result=map[boo:[1 2 3] foo:2], err=*marshmallow.MultipleLexerError
// ModeExcludeKnownFieldsFromReturnedMap and valid value: v={Foo:bar Boo:[1 2 3]}, result=map[goo:untyped], err=<nil>
}

func ExampleUnmarshalFromJSONMap() {
// enable marshmallow cache to boost up performance by reusing field type information.
marshmallow.EnableCache()
// ExampleUnmarshalFromJSONMap shows all error handling capabilities of marshmallow.UnmarshalFromJSONMap
func ExampleUnmarshalFromJSONMapErrorHandling() {
type exampleStruct struct {
Foo string `json:"foo"`
Boo []int `json:"boo"`
}

// unmarshal with mode marshmallow.ModeFailOnFirstError and valid value
// this will finish unmarshalling and return a nil err
v := flatStruct{}
result, err := marshmallow.UnmarshalFromJSONMap(
map[string]interface{}{"foo": "bar", "boo": []interface{}{float64(1), float64(2), float64(3)}}, &v)
v := exampleStruct{}
data := map[string]interface{}{"foo": "bar", "boo": []interface{}{float64(1), float64(2), float64(3)}}
result, err := marshmallow.UnmarshalFromJSONMap(data, &v)
fmt.Printf("ModeFailOnFirstError and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// unmarshal with mode marshmallow.ModeFailOnFirstError and invalid value
// this will return nil result and an error
v = flatStruct{}
result, err = marshmallow.UnmarshalFromJSONMap(
map[string]interface{}{"foo": float64(2), "boo": []interface{}{float64(1), float64(2), float64(3)}}, &v)
v = exampleStruct{}
data = map[string]interface{}{"foo": float64(2), "boo": []interface{}{float64(1), float64(2), float64(3)}}
result, err = marshmallow.UnmarshalFromJSONMap(data, &v)
fmt.Printf("ModeFailOnFirstError and invalid value: result=%+v, err=%T\n", result, err)

// unmarshal with mode marshmallow.ModeAllowMultipleErrors and valid value
// this will finish unmarshalling and return a nil err
v = flatStruct{}
v = exampleStruct{}
data = map[string]interface{}{"foo": "bar", "boo": []interface{}{float64(1), float64(2), float64(3)}}
result, err = marshmallow.UnmarshalFromJSONMap(
map[string]interface{}{"foo": "bar", "boo": []interface{}{float64(1), float64(2), float64(3)}}, &v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors))
data,
&v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors),
)
fmt.Printf("ModeAllowMultipleErrors and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// unmarshal with mode marshmallow.ModeAllowMultipleErrors and invalid value
// this will return a partially populated result and an error
v = flatStruct{}
v = exampleStruct{}
data = map[string]interface{}{"foo": float64(2), "boo": []interface{}{float64(1), float64(2), float64(3)}}
result, err = marshmallow.UnmarshalFromJSONMap(
map[string]interface{}{"foo": float64(2), "boo": []interface{}{float64(1), float64(2), float64(3)}}, &v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors))
data,
&v,
marshmallow.WithMode(marshmallow.ModeAllowMultipleErrors),
)
fmt.Printf("ModeAllowMultipleErrors and invalid value: result=%+v, err=%T\n", result, err)

// unmarshal with mode marshmallow.ModeFailOverToOriginalValue and valid value
// this will finish unmarshalling and return a nil err
v = flatStruct{}
v = exampleStruct{}
data = map[string]interface{}{"foo": "bar", "boo": []interface{}{float64(1), float64(2), float64(3)}}
result, err = marshmallow.UnmarshalFromJSONMap(
map[string]interface{}{"foo": "bar", "boo": []interface{}{float64(1), float64(2), float64(3)}}, &v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue))
data,
&v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue),
)
fmt.Printf("ModeFailOverToOriginalValue and valid value: v=%+v, result=%+v, err=%T\n", v, result, err)

// unmarshal with mode marshmallow.ModeFailOverToOriginalValue and invalid value
// this will return a fully unmarshalled result, failing to the original invalid values, and an error
v = flatStruct{}
v = exampleStruct{}
data = map[string]interface{}{"foo": float64(2), "boo": []interface{}{float64(1), float64(2), float64(3)}}
result, err = marshmallow.UnmarshalFromJSONMap(
map[string]interface{}{"foo": float64(2), "boo": []interface{}{float64(1), float64(2), float64(3)}}, &v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue))
data,
&v,
marshmallow.WithMode(marshmallow.ModeFailOverToOriginalValue),
)
fmt.Printf("ModeFailOverToOriginalValue and invalid value: result=%+v, err=%T\n", result, err)
// Output:
// ModeFailOnFirstError and valid value: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar], err=<nil>
Expand All @@ -129,11 +242,6 @@ func ExampleUnmarshalFromJSONMap() {
// ModeFailOverToOriginalValue and invalid value: result=map[boo:[1 2 3] foo:2], err=*marshmallow.MultipleError
}

type parentStruct struct {
Known string `json:"known"`
Nested childStruct `json:"nested"`
}

type childStruct struct {
Known string `json:"known"`

Expand All @@ -143,14 +251,3 @@ type childStruct struct {
func (c *childStruct) HandleJSONData(data map[string]interface{}) {
c.Data = data
}

func ExampleJSONDataHandler() {
data := []byte(`{"known": "foo","unknown": "boo","nested": {"known": "goo","unknown": "doo"}}`)
p := &parentStruct{}
_, err := marshmallow.Unmarshal(data, p)
fmt.Printf("err: %v\n", err)
fmt.Printf("nested data: %+v\n", p.Nested.Data)
// Output:
// err: <nil>
// nested data: map[known:goo unknown:doo]
}

0 comments on commit 8d6a01c

Please sign in to comment.