jsonconfig

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2025 License: MIT Imports: 5 Imported by: 5

README

jsonconfig

This package provides a convenient mechanism for using a json structure as a configuration file with the added benefit of allowing //comments.

GoDoc

Installation and Usage

To install simply call

go get github.com/callum-ramage/jsonconfig

Usage of the package couldn't be simpler

package main

import (
	"github.com/callum-ramage/jsonconfig"
	"fmt"
)

func main() {
	config, err := jsonconfig.LoadAbstract("./configs/ExampleConfig.conf", "")

	if err != nil {
		return
	}

	fmt.Println(config["example_string"].Str)
	fmt.Println(config["example_array"].Arr[0].Str)
	fmt.Println(config["example_object"].Obj["example_number"].Num)
	fmt.Println(config["example_object"].Obj["example_number"].Int)
	//Or
	fmt.Println(config["example_string"].Str)
	fmt.Println(config["example_array.0"].Str)
	fmt.Println(config["example_object.example_number"].Num)
	fmt.Println(config["example_object.example_number"].Int)
}

Outputs

string value
array value 0
5.3
5

Where ./configs/ExampleConfig.conf is

{
	"example_string": "string value",
	"example_array": [
		"array value 0"
	],
	"example_object": {
		"example_number": 5.3
	}
}

For a more detailed example that includes defining default values, have a look at jsonconfig_test.go or the GoDoc

Documentation

Overview

jsonconfig contains a set of useful structures for accessing JSON data from a configuration file. It uses a pre-processor that removes //comments from the file before parsing it.

package main

import (
  "github.com/callum-ramage/jsonconfig"
  "fmt"
)

func main() {
  config, err := jsonconfig.LoadAbstract("./configs/ExampleConfig.conf", "")

  if err != nil {
    return
  }

  fmt.Println(config["example_string"].Str)
  fmt.Println(config["example_array"].Arr[0].Str)
  fmt.Println(config["example_object"].Obj["example_number"].Num)
  fmt.Println(config["example_object"].Obj["example_number"].Int)
  //Or
  fmt.Println(config["example_string"].Str)
  fmt.Println(config["example_array.0"].Str)
  fmt.Println(config["example_object.example_number"].Num)
  fmt.Println(config["example_object.example_number"].Int)
}

Outputs

string value
array value 0
5.3
5

Where ./configs/ExampleConfig.conf is

{
  "example_string": "string value",
  "example_array": [
    "array value 0"
  ],
  "example_object": {
    "example_number": 5.3
  }
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Load

func Load(filename string, config interface{}) error

Loads the file containing a JSON object into the provided data structure. You can provide default values by defining them in the provided data structure before handing it to this func.

Example
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

type exampleObject struct {
	Example_number float64
}

type configuration struct {
	Example_string  string
	Example_array   []string
	Example_object  *exampleObject
	Example_default int
}

func main() {
	/*
	  ./configs/ExampleConfig.conf is
	  {
	    "example_string": "string value",
	    "example_array": [
	      "array value 0"
	    ],
	    "example_object": {
	      "example_number": 5.3
	    }
	  }
	*/
	config := configuration{Example_default: 4}
	err := jsonconfig.Load("./configs/ExampleConfig.conf", &config)

	if err != nil {
		return
	}

	fmt.Println("example_string:", config.Example_string)
	fmt.Println("example_array:", config.Example_array[0])
	fmt.Println("example_object:", config.Example_object.Example_number)
	fmt.Println("example_default:", config.Example_default)

}
Output:
example_string: string value
example_array: array value 0
example_object: 5.3
example_default: 4

Types

type Configuration

type Configuration map[string]JSONValue

func ConvertMap added in v1.0.2

func ConvertMap(from map[string]interface{}) Configuration

Converts an abstract map of JSON data into a map of JSONValue.

func LoadAbstract

func LoadAbstract(filename string, defaults string) (config Configuration, err error)

Loads the file containing a JSON object into an abstract map of JSONValue valueType. You can provide a default configuration by providing a partial example of the config file as a string.

Example
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

func main() {
	/*
	  ./configs/ExampleConfig.conf is
	  {
	    "example_string": "string value",
	    "example_array": [
	      "array value 0"
	    ],
	    "example_object": {
	      "example_number": 5.3
	    }
	  }
	*/
	config, err := jsonconfig.LoadAbstract("./configs/ExampleConfig.conf", `{"example_default": 4}`)

	if err != nil {
		return
	}

	fmt.Println("example_string:", config["example_string"].Str)
	fmt.Println("example_array:", config["example_array"].Arr[0].Str)
	fmt.Println("example_object:", config["example_object"].Obj["example_number"].Num)
	fmt.Println("example_default:", config["example_default"].Int)

}
Output:
example_string: string value
example_array: array value 0
example_object: 5.3
example_default: 4
Example (Arrays)
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

func main() {
	/*
	  ./configs/ExampleArrayConfig.conf is
	  {
	    "example_array": [
	      "array value 0",
	      "array value 1",
	      {
	        "handles": "objects"
	      },
	      "array value 3"
	    ]
	  }
	*/
	config, err := jsonconfig.LoadAbstract("./configs/ExampleArrayConfig.conf", "")

	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("example_array.0:", config["example_array.0"].Str)
	fmt.Println("example_array.1:", config["example_array.1"].Str)
	fmt.Println("example_array.2:", config["example_array.2"].Obj["handles.objects"].Str)
	fmt.Println("example_array.2.handles.objects:", config["example_array.2.handles.objects"].Str)
	fmt.Println("example_array.3:", config["example_array.3"].Str)

	fmt.Println("The array value that is an object wont be printed because it isn't a string")
	for _, value := range config["example_array"].Arr {
		fmt.Println(value.Str)
	}

}
Output:
example_array.0: array value 0
example_array.1: array value 1
example_array.2: even when split
example_array.2.handles.objects: even when split
example_array.3: array value 3
The array value that is an object wont be printed because it isn't a string
array value 0
array value 1

array value 3
Example (Complex)
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

func main() {
	/*
	  ./configs/ExampleComplexConfig.conf is
	  {
	    "example_object": {
	      "that": {
	        "goes": {
	          "quite": "deep"
	        }
	      },
	      "you ofcourse": "don't have to use all the depth"
	    },
	    "example_object.you ofcourse": "but collisions can be a pain"
	  }
	*/
	config, err := jsonconfig.LoadAbstract("./configs/ExampleComplexConfig.conf", "")

	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(config["example_object.that.goes.quite"].Str)
	fmt.Println(config["example_object.that.doesn't.care.how.deep.you.go.even.if.it's.invalid"].Str)
	fmt.Println(config["example_object.you ofcourse"].Str)
	fmt.Println(config["example_object"].Obj["you ofcourse"].Str)

}
Output:
deep

but collisions can be a pain
don't have to use all the depth
Example (Defaults)
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

func main() {
	/*
	  ./configs/ExampleConfig.conf is
	  {
	    "example_string": "string value",
	    "example_array": [
	      "array value 0"
	    ],
	    "example_object": {
	      "example_number": 5.3
	    }
	  }
	*/
	config, err := jsonconfig.LoadAbstract("./configs/ExampleConfig.conf", `{
      "example_default": 4,
      "example_string": "only a default",
      "example_array": [
        "arrays",
        "don't",
        "get",
        "merged"
      ],
      "example_object": {
        "example_merge": "objects get merged",
        "example_number": 6
      }
}`)

	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("example_default:", config["example_default"].Int)
	fmt.Println("example_string:", config["example_string"].Str)
	fmt.Println("length of example_array:", len(config["example_array"].Arr))
	fmt.Println("example_array:", config["example_array"].Arr[0].Str)
	fmt.Println("example_object:", config["example_object"].Obj["example_merge"].Str)
	fmt.Println("example_object:", config["example_object"].Obj["example_number"].Num)

}
Output:
example_default: 4
example_string: string value
length of example_array: 1
example_array: array value 0
example_object: objects get merged
example_object: 5.3

func LoadAbstractNoCollapse

func LoadAbstractNoCollapse(filename string, defaults string) (config Configuration, err error)

Loads the file containing a JSON object into an abstract map of JSONValue valueType. You can provide a default configuration by providing a partial example of the config file as a string. This call should be used over LoadAbstract if you wish to use range on a JSON object. The collapse performed by LoadAbstract pollutes the keys of parent objects.

func LoadString

func LoadString(JSONString string, defaults string) (config Configuration, err error)

Loads the JSON formatted string into an abstract map of JSONValue valueType. You can provide a default configuration by providing a partial example of the config file as a string.

Example
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

func main() {
	config, err := jsonconfig.LoadString(`
	{
		"example_string": "string value",
		"example_array": [
			"array value 0"
		],
		"example_object": {
			"example_number": 5.3
		}
	}
	`, `{"example_default": 4}`)

	if err != nil {
		return
	}

	fmt.Println("example_string:", config["example_string"].Str)
	fmt.Println("example_array:", config["example_array"].Arr[0].Str)
	fmt.Println("example_object:", config["example_object"].Obj["example_number"].Num)
	fmt.Println("example_default:", config["example_default"].Int)

}
Output:
example_string: string value
example_array: array value 0
example_object: 5.3
example_default: 4

func (Configuration) Collapse

func (config Configuration) Collapse()

Flattens a configuration (map[string]JSONValue) so that you can access sub levels with a "." delimeter. This function wont overwrite any keys that already exist, so if you have a structure of the form

{
  "example": {
    "collision": "ignored"
  },
  "example.collision": "used"
}

The value "used" will be returned by config["example.collision"].

func (Configuration) Get

func (config Configuration) Get(path string) JSONValue

Takes a "." delimited path and recursively uses the path, returning when a matching structure is found. So this func will return used in the following example because example.collision gets matched before example: { collision }.

{
  "example": {
    "collision": "ignored"
  },
  "example.collision": "used"
}

This func is only available because a collapsed config pollutes the parent nodes, stopping you from using

for key, value := range config {
  fmt.Println(key)
}

This func also can't handle arrays so for the following config

{
  "array": [
    {
      "value": 4
    }
  ]
}

config.Get("array.0.value").Num will return nothing while a collapsed config config["array.0.value"].Num would return 4.

func (Configuration) MergeConfig

func (config Configuration) MergeConfig(other Configuration)

Carefully copies the other Configurations values into the calling config file. If the key already exists in the calling config file then the one in the other config is ignored unless the value in the other config and the calling config are both objects. If the value is an object then the process is repeated, treating this key as a config in both the calling config and other config.

Example
package main

import (
	"fmt"

	"github.com/callum-ramage/jsonconfig"
)

func main() {
	/*
	  ./configs/ExampleConfig1.conf is
	  {
	    "from one": 1,
	    "collision": "one",
	    "object collision": {
	      "from one": 1,
	      "collision": "one",
	    },
	    "array collision": [
	      "one"
	    ]
	  }

	  ./configs/ExampleConfig2.conf is
	  {
	    "from two": 2,
	    "collision": "two",
	    "object collision": {
	      "from two": 2,
	      "collision": "two",
	    },
	    "array collision": [
	      "two",
	      "three"
	    ]
	  }
	*/
	config, err := jsonconfig.LoadAbstract("./configs/ExampleConfig1.conf", "")

	if err != nil {
		fmt.Println(err)
		return
	}

	config2, err := jsonconfig.LoadAbstract("./configs/ExampleConfig2.conf", "")

	if err != nil {
		fmt.Println(err)
		return
	}

	config.MergeConfig(config2)

	fmt.Println("from one:", config["from one"].Num)
	fmt.Println("from two:", config["from two"].Num)
	fmt.Println("collision:", config["collision"].Str)
	fmt.Println("object collision.from one:", config["object collision.from one"].Num)
	fmt.Println("object collision.from two:", config["object collision.from two"].Num)
	fmt.Println("object collision.collision:", config["object collision.collision"].Str)
	fmt.Println("length of array collision:", len(config["array collision"].Arr))
	fmt.Println("array collision.0:", config["array collision.0"].Str)

}
Output:
from one: 1
from two: 2
collision: one
object collision.from one: 1
object collision.from two: 2
object collision.collision: one
length of array collision: 1
array collision.0: one

type JSONValue

type JSONValue struct {
	Value interface{}
	Arr   []JSONValue
	Str   string
	Int   int
	Num   float64
	Bool  bool
	Obj   Configuration
}

Is a convenience struct that makes working with abstract JSON data more tolerable. The internal values Arr and Obj can be nil, so should not be assumed to be safe.

func NewJSONValue

func NewJSONValue(value interface{}) JSONValue

Creates a JSONValue from the interface provided. It attempts to fill the values Arr, Str, Int, Num, and Obj by checking against the type of the value provided.

func (JSONValue) Array

func (key JSONValue) Array() []JSONValue

Checks if the type of the JSON value is an array and if appropriate, casts it into an array of JSONValue.

func (JSONValue) Boolean

func (key JSONValue) Boolean() bool

Checks if the type of the JSON value is a bool and if appropriate, casts it into a bool.

func (JSONValue) Integer

func (key JSONValue) Integer() int

Checks if the type of the JSON value is a float64 and if appropriate, casts it into an int.

func (JSONValue) Number

func (key JSONValue) Number() float64

Checks if the type of the JSON value is a float64 and if appropriate, casts it into a float64.

func (JSONValue) Object

func (key JSONValue) Object() Configuration

Checks if the type of the JSON value is an object and if appropriate, casts it into a map of JSONValue.

func (JSONValue) String

func (key JSONValue) String() string

Checks if the type of the JSON value is a string and if appropriate, casts it into a string.

type JsonCommentStripper

type JsonCommentStripper struct {
	R io.Reader
	// contains filtered or unexported fields
}

Outputs json with //comments removed.

func NewJsonCommentStripper

func NewJsonCommentStripper(reader io.Reader) *JsonCommentStripper

Creates a new comment stripper that can be used as an intermediate layer between a JSON decoder and a json source reader.

func (*JsonCommentStripper) Read

func (j *JsonCommentStripper) Read(p []byte) (n int, err error)

Reads data from the internal reader, removing //comments as it goes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL