Giter Site home page Giter Site logo

hjson-go's Introduction

hjson-go

Hjson Intro

{
  # specify rate in requests/second (because comments are helpful!)
  rate: 1000

  // prefer c-style comments?
  /* feeling old fashioned? */

  # did you notice that rate doesn't need quotes?
  hey: look ma, no quotes for strings either!

  # best of all
  notice: []
  anything: ?

  # yes, commas are optional!
}

The Go implementation of Hjson is based on hjson-js. For other platforms see hjson.github.io.

More documentation can be found at https://pkg.go.dev/github.com/goldstd/hjson-go

Install

Instructions for installing a pre-built hjson tool can be found at https://hjson.github.io/users-bin.html

If you instead want to build locally, make sure you have a working Go environment. See the install instructions.

  • In order to use Hjson from your own Go source code, just add an import line like the one here below. Before building your project, run go mod tidy in order to download the Hjson source files. The suffix /v4 is required in the import path, unless you specifically want to use an older major version.
import "github.com/goldstd/hjson-go"
  • If you instead want to use the hjson command line tool, run the command here below in your terminal. The executable will be installed into your go/bin folder, make sure that folder is included in your PATH environment variable.
go install github.com/goldstd/hjson-go/hjson@latest

Usage as command line tool

usage: hjson [OPTIONS] [INPUT]
hjson can be used to convert JSON from/to Hjson.

hjson will read the given JSON/Hjson input file or read from stdin.

Options:
  -bracesSameLine
        Print braces on the same line.
  -c    Output as JSON.
  -eof string
        End of line, should be either \n or \r\n (default "\n")
  -h    Show this screen.
  -indent string
        The indent string. (default "  ")
  -j    Output as formatted JSON.
  -omitRootBraces
        Omit braces at the root.
  -preserveKeyOrder
        Preserve key order in objects/maps.
  -quoteAlways
        Always quote string values.
  -v    Show version.
$ jj -gu a=@姓名 'b=@汉字(3)' c=@性别 d=@地址 | hjson -eof ', ' -omitRootBraces
a: 许鑀邩, b: 皿鐘獢, c: 男, d: 四川省遂宁市邚軫路7364号勳鐆小区8单元2202室

Sample:

  • run hjson test.json > test.hjson to convert to Hjson
  • run hjson -j test.hjson > test.json to convert to JSON

Usage as a GO library

package main

import (
    "github.com/goldstd/hjson-go"
    "fmt"
)

func main() {
    // Now let's look at decoding Hjson data into Go
    // values.
    sampleText := []byte(`
    {
        # specify rate in requests/second
        rate: 1000
        array:
        [
            foo
            bar
        ]
    }`)

    // We need to provide a variable where Hjson
    // can put the decoded data.
    var dat map[string]interface{}

    // Decode with default options and check for errors.
    if err := hjson.Unmarshal(sampleText, &dat); err != nil {
        panic(err)
    }
    // short for:
    // options := hjson.DefaultDecoderOptions()
    // err := hjson.UnmarshalWithOptions(sampleText, &dat, options)
    fmt.Println(dat)

    // In order to use the values in the decoded map,
    // we'll need to cast them to their appropriate type.

    rate := dat["rate"].(float64)
    fmt.Println(rate)

    array := dat["array"].([]interface{})
    str1 := array[0].(string)
    fmt.Println(str1)


    // To encode to Hjson with default options:
    sampleMap := map[string]int{"apple": 5, "lettuce": 7}
    hjson, _ := hjson.Marshal(sampleMap)
    // short for:
    // options := hjson.DefaultOptions()
    // hjson, _ := hjson.MarshalWithOptions(sampleMap, options)
    fmt.Println(string(hjson))
}

Unmarshal to Go structs

If you prefer, you can also unmarshal to Go structs (including structs implementing the json.Unmarshaler interface or the encoding.TextUnmarshaler interface). The Go JSON package is used for this, so the same rules apply. Specifically for the "json" key in struct field tags. For more details about this type of unmarshalling, see the documentation for json.Unmarshal().

package main

import (
    "github.com/goldstd/hjson-go"
    "fmt"
)

type Sample struct {
    Rate  int
    Array []string
}

type SampleAlias struct {
    Rett    int      `json:"rate"`
    Ashtray []string `json:"array"`
}

func main() {
    sampleText := []byte(`
    {
        # specify rate in requests/second
        rate: 1000
        array:
        [
            foo
            bar
        ]
    }`)

    // unmarshal
    var sample Sample
    hjson.Unmarshal(sampleText, &sample)

    fmt.Println(sample.Rate)
    fmt.Println(sample.Array)

    // unmarshal using json tags on struct fields
    var sampleAlias SampleAlias
    hjson.Unmarshal(sampleText, &sampleAlias)

    fmt.Println(sampleAlias.Rett)
    fmt.Println(sampleAlias.Ashtray)
}

Comments on struct fields

By using key comment in struct field tags you can specify comments to be written on one or more lines preceding the struct field in the Hjson output. Another way to output comments is to use hjson.Node structs, more on than later.

package main

import (
    "github.com/goldstd/hjson-go"
    "fmt"
)

type foo struct {
    A string `json:"x" comment:"First comment"`
    B int32  `comment:"Second comment\nLook ma, new lines"`
    C string
    D int32
}

func main() {
    a := foo{A: "hi!", B: 3, C: "some text", D: 5}
    buf, err := hjson.Marshal(a)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(string(buf))
}

Output:

{
  # First comment
  x: hi!

  # Second comment
  # Look ma, new lines
  B: 3

  C: some text
  D: 5
}

Read and write comments

The only way to read comments from Hjson input is to use a destination variable of type hjson.Node or *hjson.Node. The hjson.Node must be the root destination, it won't work if you create a field of type hjson.Node in some other struct and use that struct as destination. An hjson.Node struct is simply a wrapper for a value and comments stored in an hjson.Comments struct. It also has several convenience functions, for example AtIndex() or SetKey() that can be used when you know that the node contains a value of type []interface{} or *hjson.OrderedMap. All of the elements in []interface{} or *hjson.OrderedMap will be of type *hjson.Node in trees created by hjson.Unmarshal, but the hjson.Node convenience functions unpack the actual values from them.

When hjson.Node or *hjson.Node is used as destination for Hjson unmarshal the output will be a tree of *hjson.Node where all of the values contained in tree nodes will be of these types:

  • nil (no type)
  • float64   (if UseJSONNumber == false)
  • json.Number   (if UseJSONNumber == true)
  • string
  • bool
  • []interface{}
  • *hjson.OrderedMap

These are just the types used by Hjson unmarshal and the convenience functions, you are free to assign any type of values to nodes in your own code.

The comments will contain all whitespace chars too (including line feeds) so that an Hjson document can be read and written without altering the layout. This can be disabled by setting the decoding option WhitespaceAsComments to false.

package main

import (
    "fmt"

    "github.com/goldstd/hjson-go"
)

func main() {
    // Now let's look at decoding Hjson data into hjson.Node.
    sampleText := []byte(`
    {
        # specify rate in requests/second
        rate: 1000
        array:
        [
            foo
            bar
        ]
    }`)

    var node hjson.Node
    if err := hjson.Unmarshal(sampleText, &node); err != nil {
        panic(err)
    }

    node.NK("array").Cm.Before = `        # please specify an array
        `

    if _, _, err := node.NKC("subMap").SetKey("subVal", 1); err != nil {
        panic(err)
    }

    outBytes, err := hjson.Marshal(node)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(outBytes))
}

Output:


    {
        # specify rate in requests/second
        rate: 1000
        # please specify an array
        array:
        [
            foo
            bar
        ]
  subMap: {
    subVal: 1
  }
    }

Type ambiguity

Hjson allows quoteless strings. But if a value is a valid number, boolean or null then it will be unmarshalled into that type instead of a string when unmarshalling into interface{}. This can lead to unintended consequences if the creator of an Hjson file meant to write a string but didn't think of that the quoteless string they wrote also was a valid number.

The ambiguity can be avoided by using typed destinations when unmarshalling. A string destination will receive a string even if the quoteless string also was a valid number, boolean or null. Example:

package main

import (
    "github.com/goldstd/hjson-go"
    "fmt"
)

type foo struct {
  A string
}

func main() {
    var dest foo
    err := hjson.Unmarshal([]byte(`a: 3`), &dest)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(dest)
}

Output:

{3}

String pointer destinations can be set to nil by writing null in an Hjson file. The same goes for a pointer destination of any type that implements UnmarshalText().

ElemTyper interface

If a destination type implements hjson.ElemTyper, Unmarshal() will call ElemType() on the destination when unmarshalling an array or an object, to see if any array element or leaf node should be of type string even if it can be treated as a number, boolean or null. This is most useful if the destination also implements the json.Unmarshaler interface, because then there is no other way for Unmarshal() to know the type of the elements on the destination. If a destination implements ElemTyper all of its elements must be of the same type.

Example implementation for a generic ordered map:

func (o *OrderedMap[T]) ElemType() reflect.Type {
  return reflect.TypeOf((*T)(nil)).Elem()
}

API

godoc

History

see releases

hjson-go's People

Contributors

laktak avatar trobro avatar dqsully avatar leoriether avatar mspiegel avatar bingoohuang avatar 0xflotus avatar alpancs avatar antonoks avatar gabrielnicolasavellaneda avatar jcgruenhage avatar neilalexander avatar bronze1man avatar dimiro1 avatar hmalphettes avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.