diff --git a/README.md b/README.md
index 2eee63f..96503de 100644
--- a/README.md
+++ b/README.md
@@ -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)
@@ -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"`
@@ -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.
@@ -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 Creative Commons Attribution 4.0 International License.
diff --git a/example_test.go b/example_test.go
index bfbde6b..a35cbfa 100644
--- a/example_test.go
+++ b/example_test.go
@@ -3,64 +3,163 @@ 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=
+}
+
+// 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:
+ // 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=
// ModeFailOnFirstError and invalid value: result=map[], err=*jlexer.LexerError
@@ -68,57 +167,71 @@ func ExampleUnmarshal() {
// 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=
// 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=
}
-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=
@@ -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"`
@@ -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:
- // nested data: map[known:goo unknown:doo]
-}