conjson

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 16, 2019 License: MIT Imports: 2 Imported by: 15

README

conjson

Build Status Coverage Status Go Report Card GoDoc Latest Stable Version

conjson - (conventional, consistent, conformative) JSON

A simple, functional, no-tags-required mechanism to handle and transform JSON representations of values, consistently.

Project Status

This project is currently in "pre-release". While the code is heavily tested, the API may change. Vendor or "lock" this dependency if you plan on using it.

History and Motivation

This project was originally born from a 3+ year old (at the time of creation) "Gist".

Both that Gist, and eventually this larger project, were inspired by a desire to more easily work with APIs that accepted/returned JSON that had "snake_case"-style object keys.

Basically, I wanted a way to Marshal and Unmarshal Go structures without having to add "tags" to each and every field of each and every structure. That Gist solved that problem for me, and now this library can do the same but with more power and flexibility.

Examples

Marshal a Go structure into "conventional" style JSON
model := exampleModel{
	Title:         "Example Title",
	Description:   "This is a description.",
	ImageURL:      "https://example.com/image.png",
	ReferredByURL: "https://example.com/referrer/index.html",
	IsActive:      true,
	CreatedAt:     inceptionTime,
	UpdatedAt:     packageTime,
}

marshaler := conjson.NewMarshaler(model, transform.ConventionalKeys())

encoded, _ := json.MarshalIndent(marshaler, marshalPrefix, marshalIndent)

fmt.Println(string(encoded))
// Output:
// {
//     "title": "Example Title",
//     "description": "This is a description.",
//     "image_url": "https://example.com/image.png",
//     "referred_by_url": "https://example.com/referrer/index.html",
//     "is_active": true,
//     "created_at": "2015-11-17T20:43:31-05:00",
//     "updated_at": "2018-12-24T13:21:15-07:00"
// }
Unmarshal "conventional" style JSON into a Go structure
sampleJSON := `
{
	"title": "Example Title",
	"description": "This is a description.",
	"image_url": "https://example.com/image.png",
	"referred_by_url": "https://example.com/referrer/index.html",
	"is_active": true,
	"created_at": "2015-11-17T20:43:31-05:00",
	"updated_at": "2018-12-24T13:21:15-07:00"
}
`

var model exampleModel

json.Unmarshal(
	[]byte(sampleJSON),
	conjson.NewUnmarshaler(&model, transform.ConventionalKeys()),
)

// Print the "raw" model JSON to show result
rawJSON, _ := json.MarshalIndent(model, marshalPrefix, marshalIndent)
fmt.Println(string(rawJSON))
// Output:
// {
//     "Title": "Example Title",
//     "Description": "This is a description.",
//     "ImageURL": "https://example.com/image.png",
//     "ReferredByURL": "https://example.com/referrer/index.html",
//     "IsActive": true,
//     "CreatedAt": "2015-11-17T20:43:31-05:00",
//     "UpdatedAt": "2018-12-24T13:21:15-07:00"
// }
Encode a Go structure into "camelCase" style JSON
model := exampleModel{
	Title:         "Example Title",
	Description:   "This is a description.",
	ImageURL:      "https://example.com/image.png",
	ReferredByURL: "https://example.com/referrer/index.html",
	IsActive:      true,
	CreatedAt:     inceptionTime,
	UpdatedAt:     packageTime,
}

jsonEncoder := json.NewEncoder(os.Stdout)
jsonEncoder.SetIndent(marshalPrefix, marshalIndent)

conjson.NewEncoder(jsonEncoder, transform.CamelCaseKeys(false)).Encode(model)

// Output:
// {
//     "title": "Example Title",
//     "description": "This is a description.",
//     "imageURL": "https://example.com/image.png",
//     "referredByURL": "https://example.com/referrer/index.html",
//     "isActive": true,
//     "createdAt": "2015-11-17T20:43:31-05:00",
//     "updatedAt": "2018-12-24T13:21:15-07:00"
// }
Decode JSON with atypical keys into a Go structure
sampleJSON := `
{
	"$title--": "Example Title",
	"$description--": "This is a description.",
	"$image_url--": "https://example.com/image.png",
	"$referred_by_url--": "https://example.com/referrer/index.html",
	"$is_active--": true,
	"created_at--": "2015-11-17T20:43:31-05:00",
	"updated_at--": "2018-12-24T13:21:15-07:00"
}
`

var model exampleModel

decoder := conjson.NewDecoder(
	json.NewDecoder(bytes.NewBufferString(sampleJSON)),
	transform.ConventionalKeys(),
	transform.ValidIdentifierKeys(),
)

decoder.Decode(&model)

// Print the "raw" model JSON to show result
rawJSON, _ := json.MarshalIndent(model, marshalPrefix, marshalIndent)
fmt.Println(string(rawJSON))
// Output:
// {
//     "Title": "Example Title",
//     "Description": "This is a description.",
//     "ImageURL": "https://example.com/image.png",
//     "ReferredByURL": "https://example.com/referrer/index.html",
//     "IsActive": true,
//     "CreatedAt": "2015-11-17T20:43:31-05:00",
//     "UpdatedAt": "2018-12-24T13:21:15-07:00"
// }

Documentation

Overview

Package conjson provides a simple, functional, no-tags-required mechanism to handle and transform JSON representations of values, consistently.

The types in this package provide JSON Encoder and Decoder wrappers, along with Marshaler and Unmarshaler implementations, that allow for the transformation of the JSON data that is used in those mechanisms.

The examples may provide more obvious context and reasoning for the use of these transformations.

Copyright © Trevor N. Suarez (Rican7)

Example (Marshal)
package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/Rican7/conjson"
	"github.com/Rican7/conjson/transform"
)

type exampleModel struct {
	Title         string
	Description   string
	ImageURL      string
	ReferredByURL string
	IsActive      bool
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

const (
	dateTimeFormat = time.RFC3339

	inceptionDateTime = "2015-11-17T20:43:31-05:00"
	packageDateTime   = "2018-12-24T13:21:15-07:00"

	marshalPrefix = ""
	marshalIndent = "    "
)

var (
	inceptionTime, _ = time.Parse(dateTimeFormat, inceptionDateTime)
	packageTime, _   = time.Parse(dateTimeFormat, packageDateTime)
)

func main() {
	model := exampleModel{
		Title:         "Example Title",
		Description:   "This is a description.",
		ImageURL:      "https://example.com/image.png",
		ReferredByURL: "https://example.com/referrer/index.html",
		IsActive:      true,
		CreatedAt:     inceptionTime,
		UpdatedAt:     packageTime,
	}

	marshaler := conjson.NewMarshaler(model, transform.ConventionalKeys())

	encoded, _ := json.MarshalIndent(marshaler, marshalPrefix, marshalIndent)

	fmt.Println(string(encoded))
}
Output:

{
    "title": "Example Title",
    "description": "This is a description.",
    "image_url": "https://example.com/image.png",
    "referred_by_url": "https://example.com/referrer/index.html",
    "is_active": true,
    "created_at": "2015-11-17T20:43:31-05:00",
    "updated_at": "2018-12-24T13:21:15-07:00"
}
Example (Unmarshal)
package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/Rican7/conjson"
	"github.com/Rican7/conjson/transform"
)

type exampleModel struct {
	Title         string
	Description   string
	ImageURL      string
	ReferredByURL string
	IsActive      bool
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

const (
	marshalPrefix = ""
	marshalIndent = "    "
)

func main() {
	sampleJSON := `
{
	"title": "Example Title",
	"description": "This is a description.",
	"image_url": "https://example.com/image.png",
	"referred_by_url": "https://example.com/referrer/index.html",
	"is_active": true,
	"created_at": "2015-11-17T20:43:31-05:00",
	"updated_at": "2018-12-24T13:21:15-07:00"
}
`

	var model exampleModel

	json.Unmarshal(
		[]byte(sampleJSON),
		conjson.NewUnmarshaler(&model, transform.ConventionalKeys()),
	)

	// Print the "raw" model JSON to show result
	rawJSON, _ := json.MarshalIndent(model, marshalPrefix, marshalIndent)
	fmt.Println(string(rawJSON))
}
Output:

{
    "Title": "Example Title",
    "Description": "This is a description.",
    "ImageURL": "https://example.com/image.png",
    "ReferredByURL": "https://example.com/referrer/index.html",
    "IsActive": true,
    "CreatedAt": "2015-11-17T20:43:31-05:00",
    "UpdatedAt": "2018-12-24T13:21:15-07:00"
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewMarshaler

func NewMarshaler(value interface{}, transformers ...transform.Transformer) json.Marshaler

NewMarshaler takes a value and a variable number of `transform.Transformer`s and returns an `encoding/json.Marshaler` that runs the given transformers upon JSON marshaling.

See the documentation for both `encoding/json.Marshaler` and `encoding/json.Marshal` for more details about JSON marshaling.

func NewUnmarshaler

func NewUnmarshaler(value interface{}, transformers ...transform.Transformer) json.Unmarshaler

NewUnmarshaler takes a pointer value and a variable number of `transform.Transformer`s and returns an `encoding/json.Unmarshaler` that runs the given transformers upon JSON unmarshaling.

See the documentation for both `encoding/json.Unmarshaler` and `encoding/json.Unmarshal` for more details about JSON unmarshaling.

Types

type Decoder

type Decoder interface {
	// Decode reads the next JSON represented value from the underlying/inner
	// decoder and stores the decoded result in the pointed to passed value.
	//
	// See the documentation for both `encoding/json.Decoder` and
	// `encoding/json.Unmarshal` for more details about the workings of the
	// underlying decoder.
	Decode(interface{}) error
}

Decoder is an interface defining a simple JSON decoder, with an interface compatible with `encoding/json.Decoder`.

Example
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"github.com/Rican7/conjson"
	"github.com/Rican7/conjson/transform"
)

type exampleModel struct {
	Title         string
	Description   string
	ImageURL      string
	ReferredByURL string
	IsActive      bool
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

const (
	marshalPrefix = ""
	marshalIndent = "    "
)

func main() {
	sampleJSON := `
{
	"$title--": "Example Title",
	"$description--": "This is a description.",
	"$image_url--": "https://example.com/image.png",
	"$referred_by_url--": "https://example.com/referrer/index.html",
	"$is_active--": true,
	"created_at--": "2015-11-17T20:43:31-05:00",
	"updated_at--": "2018-12-24T13:21:15-07:00"
}
`

	var model exampleModel

	decoder := conjson.NewDecoder(
		json.NewDecoder(bytes.NewBufferString(sampleJSON)),
		transform.ConventionalKeys(),
		transform.ValidIdentifierKeys(),
	)

	decoder.Decode(&model)

	// Print the "raw" model JSON to show result
	rawJSON, _ := json.MarshalIndent(model, marshalPrefix, marshalIndent)
	fmt.Println(string(rawJSON))
}
Output:

{
    "Title": "Example Title",
    "Description": "This is a description.",
    "ImageURL": "https://example.com/image.png",
    "ReferredByURL": "https://example.com/referrer/index.html",
    "IsActive": true,
    "CreatedAt": "2015-11-17T20:43:31-05:00",
    "UpdatedAt": "2018-12-24T13:21:15-07:00"
}

func NewDecoder

func NewDecoder(inner *json.Decoder, transformers ...transform.Transformer) Decoder

NewDecoder takes an `encoding/json.Decoder` and a variable number of `transform.Transformer`s and returns an `Decoder` that runs the given transformers upon JSON encoding.

See the documentation for both `encoding/json.Decoder` and `encoding/json.Unmarshal` for more details about the passed inner decoder.

type Encoder

type Encoder interface {
	// Encode takes a value and encodes the JSON representation of that value to
	// the underlying/inner encoder.
	//
	// See the documentation for both `encoding/json.Encoder` and
	// `encoding/json.Marshal` for more details about the workings of the
	// underlying encoder.
	Encode(interface{}) error
}

Encoder is an interface defining a simple JSON encoder, with an interface compatible with `encoding/json.Encoder`.

Example
package main

import (
	"encoding/json"
	"os"
	"time"

	"github.com/Rican7/conjson"
	"github.com/Rican7/conjson/transform"
)

type exampleModel struct {
	Title         string
	Description   string
	ImageURL      string
	ReferredByURL string
	IsActive      bool
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

const (
	dateTimeFormat = time.RFC3339

	inceptionDateTime = "2015-11-17T20:43:31-05:00"
	packageDateTime   = "2018-12-24T13:21:15-07:00"

	marshalPrefix = ""
	marshalIndent = "    "
)

var (
	inceptionTime, _ = time.Parse(dateTimeFormat, inceptionDateTime)
	packageTime, _   = time.Parse(dateTimeFormat, packageDateTime)
)

func main() {
	model := exampleModel{
		Title:         "Example Title",
		Description:   "This is a description.",
		ImageURL:      "https://example.com/image.png",
		ReferredByURL: "https://example.com/referrer/index.html",
		IsActive:      true,
		CreatedAt:     inceptionTime,
		UpdatedAt:     packageTime,
	}

	jsonEncoder := json.NewEncoder(os.Stdout)
	jsonEncoder.SetIndent(marshalPrefix, marshalIndent)

	conjson.NewEncoder(jsonEncoder, transform.CamelCaseKeys(false)).Encode(model)

}
Output:

{
    "title": "Example Title",
    "description": "This is a description.",
    "imageURL": "https://example.com/image.png",
    "referredByURL": "https://example.com/referrer/index.html",
    "isActive": true,
    "createdAt": "2015-11-17T20:43:31-05:00",
    "updatedAt": "2018-12-24T13:21:15-07:00"
}

func NewEncoder

func NewEncoder(inner *json.Encoder, transformers ...transform.Transformer) Encoder

NewEncoder takes an `encoding/json.Encoder` and a variable number of `transform.Transformer`s and returns an `Encoder` that runs the given transformers upon JSON encoding.

See the documentation for both `encoding/json.Encoder` and `encoding/json.Marshal` for more details about the passed inner encoder.

Directories

Path Synopsis
Package transform provides mechanisms to transform a data source, with a specific focus on JSON data handling.
Package transform provides mechanisms to transform a data source, with a specific focus on JSON data handling.

Jump to

Keyboard shortcuts

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