Categorygithub.com/swaros/contxt/module/yamc
modulepackage
0.0.1
Repository: https://github.com/swaros/contxt.git
Documentation: pkg.go.dev

# README

Yet Another Map Converter

Description

this library is build to avoid the need of knowing the data structure for reading data files in different formats. so the problem is depending on two types of data structures you need to know to read data from a source and use them in your code:

  • Array: which is a list of values of the same type.
  • Object: which is a list of key-value pairs.

The Issue

for parsing unknown source data, you run in trouble, if you don't know the "root" data structure.

objects read as map[string]interface{}

{
    "name": "John Doe",
    "age": 30,
    "cars": [
        "Ford",
        "BMW",
        "Fiat"
    ]
}

and arrays read as []interface{}

[
    "Ford",
    "BMW",
    "Fiat"
]

so you need to know the data structure to read the data, and use it in your code. or you will get an error. like this one:

jsonSource := []byte(`[
		{
			"id": 1,
			"name": "John Doe",
			"email": "[email protected]",
			"phone": "555-555-5555"
		},
		{
			"id": 2,
			"name": "Jane Doe",
			"email": "[email protected]",
			"phone": "555-555-5521"
		},
		{
			"id": 3,
			"name": "John Smith",
			"email": "[email protected]",
			"phone": "888-555-5555"
		}
	]`)

	var data map[string]interface{}
	if err := json.Unmarshal(jsonSource, &data); err != nil {
		panic(err)
	}

this would result in the following error:

panic: json: cannot unmarshal array into Go value of type map[string]interface {}

the code is obvious, we are trying to unmarshal an array into a map, which is not possible. all of them is fine if we can trust the source data, but what if we can't? for example to have some more generic code, or to read data from a user input, or a file, or a database, or a network connection, or ... .

The Solution

YAMC is an abbreviation for Yet Another Map Converter, which is a library to convert data from one data structure to another. it is a simple library, which is build to solve the problem of reading data from unknown sources, and convert them to a known data structure, so you can use them in your code.

the goal is to have a library that reads data from different sources, depending on Readers specialized to a Datatype (json yaml and so on) , and convert them to a known data structure, and provide a simple API to get the data from the source.

one usecase is to have no longer taking care about value files they are used together with go/template, and you even not know what value files provide what data, and how they paths are composed. because booth is then depending the specific usage later on.

depending this data:

for the example we use just an byte array.

jsonSource := []byte(`[
   {
        "id": 1,
        "name": "John Doe",
        "email": ""
    },
]`)

Create an instance of Yamc

mapData := yamc.New()

use an Reader to parse the data


if err := mapData.Parse(yamc.NewJsonReader(), jsonSource); err != nil {
    panic(err)
}

get what structure is used

this is useful if you want to know what data structure was used to parse the data. at least if you have to compose paths to get the data from the source.

switch mapData.GetSourceDataType() {
case yamc.TYPE_ARRAY:
	fmt.Println("source data was in form of []interface{}")
case yamc.TYPE_STRING_MAP:
	fmt.Println("source data was in form of map[string]interface{}")
default:
	fmt.Println("this should not happen")
}

get the data from the source

a simple dotted path where each string is used as keyname, and each number is used as index. 0.name would be the name of the first element in the array. index 0 the element named "name".

data, err := mapData.Get("0.name")
if err != nil {
    panic(err)
}
fmt.Println(fmt.Sprintf("%v", data))

more complex example paths

for more complex paths, YAMC implements tidwall/gson so you can use the same syntax as in gson.

source := []byte(`{
	"name": {"first": "Tom", "last": "Anderson"},
	"age":37,
	"children": ["Sara","Alex","Jack"],
	"fav.movie": "Deer Hunter",
	"friends": [
	  {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
	  {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
	  {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
	]
  }`)

data := yamc.New()
data.Parse(yamc.NewJsonReader(), source)

paths := []string{
	"name.last",
	"age",
	"children",
	"children.#",
	"children.1",
	"child*.2",
	"c?ildren.0",
	"fav\\.movie",
	"friends.#.first",
	"friends.1.last",
	"friends.#(last==\"Murphy\").first",
	"friends.#(last==\"Murphy\")#.first",
	"friends.#(age>45)#.last",
	"friends.#(first%\"D*\").last",
	"friends.#(first!%\"D*\").last",
	"friends.#(nets.#(==\"fb\"))#.first",
}
// just use any of the examples from gson README.md (https://github.com/tidwall/gjson)
for _, gsonPath := range paths {
	name, _ := data.GetGjsonString(gsonPath)	
	fmt.Println(gsonPath, " => ", name)
}

see in example/gson/main.go.

conversion

YAMC depending of the reader interface. there a 2 implementations for json and yaml. so you can read data from one source and convert it to another format.

data := []byte(`{"age": 45, "hobbies": ["golf", "reading", "swimming"]}`)
conv := yamc.New()
if err := conv.Parse(yamc.NewJsonReader(), data); err != nil {
	panic(err)
} else {
	if str, err2 := conv.ToString(yamc.NewYamlReader()); err2 != nil {
		panic(err2)
	} else {
		fmt.Println(str)
	}
}

will result in:

age: 45
hobbies:
    - golf
    - reading
    - swimming

# Packages

No description provided by the author

# Functions

CanHandleValue just make sure we do not run in panic because of an dataype we nor able to handle.
FindChain is trying to get a value from different types of slices and maps.
IsPointer checks if the given interface is a pointer.
New returns a new Yamc instance.
NewByFile loads file content and returns a new Ymac.
NewByJson json file loading shortcut.
NewByYaml shortcut for reading Yaml File by using NewYmacByFile.
No description provided by the author
NewStructDef returns a new struct reader.
NewYamlReader creates a new YamlReader.

# Constants

source type []interface{}.
..
initial status.

# Structs

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
YamlReader is a reader for yaml files.

# Interfaces

DataReader is the interface for the data reader the data reader have to support the regular unmarshal and marshal.