JSON:API compatible query string parser

Related tags

JSON qparser
Overview

QParser

The package helps to parse part of the URL path and its parameters string to a handy structure. The structure format is compatible with the JSON:API specification. QParser will be useful both for implementing the API according to this specification and independently.

0 { fmt.Printf("3. sort by %s %s\n", sort[0].FieldName, sort[0].Order) fmt.Printf("4. is descending order? %t", sort[0].Order == qparser.OrderDesc) } payload, _ := json.MarshalIndent(request, "", " ") fmt.Printf("\nThe structure:\n\n%s", payload) ">
	params := "/articles/42?fields[articles]=title,body&include=comments.author&filter[createdAt]=lt:2015-10-02&sort=-createdAt"

	request, _ := qparser.ParseRequest(params)

	values := request.Query.Values
	fmt.Printf("1. raw value of fields[articles] is %q\n", values.Get("fields", "articles"))
	fmt.Printf("2. the list of requested fields for the articles resource is %v\n", request.Query.Fields["articles"])

	sort := request.Query.Sort
	if len(sort) > 0 {
		fmt.Printf("3. sort by %s %s\n", sort[0].FieldName, sort[0].Order)
		fmt.Printf("4. is descending order? %t", sort[0].Order == qparser.OrderDesc)
	}

	payload, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("\nThe structure:\n\n%s", payload)

The example will print:

1. raw value of fields[articles] is "title,body"
2. the list of requested fields for the articles resource is [title body]
3. sort by createdAt DESC
4. is descending order? true
The structure:

{
  "Resource": {
    "Type": "articles",
    "ID": "42"
  },
  "RelationshipType": "",
  "RelatedResourceType": "",
  "Query": {
    "Includes": [
      {
        "Relation": "comments",
        "Includes": [
          {
            "Relation": "author",
            "Includes": null
          }
        ]
      }
    ],
    "Fields": {
      "articles": [
        "title",
        "body"
      ]
    },
    "Sort": [
      {
        "FieldName": "createdAt",
        "Order": 1
      }
    ],
    "Page": null,
    "Values": {
      "fields": [
        {
          "TopLevelKey": "fields",
          "NestedKeys": [
            "articles"
          ],
          "Value": "title,body"
        }
      ],
      "filter": [
        {
          "TopLevelKey": "filter",
          "NestedKeys": [
            "createdAt"
          ],
          "Value": "lt:2015-10-02"
        }
      ],
      "include": [
        {
          "TopLevelKey": "include",
          "NestedKeys": null,
          "Value": "comments.author"
        }
      ],
      "sort": [
        {
          "TopLevelKey": "sort",
          "NestedKeys": null,
          "Value": "-createdAt"
        }
      ]
    }
  }
}

Using the "Values"

The package fundamental types are the "Value" struct, and the "Values" map. The Value type is used to represent key-value pairs "key=value",
with the addition of "nested keys" "key[nested_key]=value". The types provide convenient way for accessing properties of the query string of this kind "style[top][color]=white&style[size]=XL".

The string is split into substrings separated by ampersands '&' or semicolons ';'. The key is the part of the substring up to the equal sign '='. Anything that goes after the equal sign is interpreted as a value. Substrings in the key part surrounded by square brackets '\ [', ']' are interpreted as nested keys.

A setting without an equals sign is interpreted as a key set to an empty value.

To get a map of values from a string, call the "ParseValues" function.

	q := "style[top][color]=white&style[size]=XL"
	values, _ := qparser.ParseValues(q)

	fmt.Printf(
		"Top color is %q, size is %q\n",
		values.Get("style", "top", "color"),
		values.Get("style", "size"),
	)
    // prints: Top color is "white", size is "XL"

To access multiple values or check a key presence use the map directly.

0 { continue } fmt.Println(size.Value) } } // prints: // // the list of sizes: // L // XL ">
	q := "size=L&size=XL"
	values, _ := qparser.ParseValues(q)

	if list, ok := values["size"]; ok {
		fmt.Println("the list of sizes: ")
		for _, size := range list {
			if len(size.NestedKeys) > 0 {
				continue
			}
			fmt.Println(size.Value)
		}
	}
    // prints:
    //
    // the list of sizes: 
    // L
    // XL

The "Query" structure

The "Query" structure adds some extras. The "ParseQuery" function additionally processes the values of the following query keys:

  • include
  • fields[resource_type]
  • sort
  • filter[field_name]
  • page

Includes

The value of the include key is considered as a request to add resources related to the requested resource. For example, a client requests an article and asks to include in the response the data of the author of the article, comments on this article and data about the author of the comment. The request might look like this "articles/42?include=author,comments.author".

	pathAndQuery := "articles/42?include=author,comments.author"
	q := pathAndQuery[strings.IndexByte(pathAndQuery, '?'):] // separate the path from the query

	query, _ := qparser.ParseQuery(q)

	includes, _ := json.MarshalIndent(query.Includes, "", "  ")
	fmt.Println(string(includes))

The above example outputs:

[
  {
    "Relation": "author",
    "Includes": null
  },
  {
    "Relation": "comments",
    "Includes": [
      {
        "Relation": "author",
        "Includes": null
      }
    ]
  }
]

The hierarchy of this recursive structure represents the resources that needs to be included in the response.

The calling code can iterate over this structure to implement the desired data loads.

Note that QParser does not limit the depth of inclusions. Any constraints and checks must be done in the calling code.

Fields

It is assumed that the fields query parameter is used to specify the list of attributes of the requested resource. The "fields" parameter must specify the resource type as a nested key e.g. "fields[articles]=title,body". There should be exactly one nested key. Any other form of the "fields" parameter is ignored.

	q := "fields[articles]=title,body&fields[author]=name,dob"

	query, _ := qparser.ParseQuery(q)

	fields, _ := json.MarshalIndent(query.Fields, "", "  ")
	fmt.Println(string(fields))

Prints:

{
  "articles": [
    "title",
    "body"
  ],
  "author": [
    "name",
    "dob"
  ]
}

Sort

The value of the "sort" query parameter represents sort fields separated by the comma. The sort order is ascending by default. In order to specify descending sort order the sort field must be prefixed with the minus sign. For instance "sort=-createdAt,title" means to sort a list from the latest to newest and then by title in the ascending order.

	q := "sort=-createdAt,title"

	query, _ := qparser.ParseQuery(q)

	for _, sort := range query.Sort {
		fmt.Printf("sort by %q %q \n", sort.FieldName, sort.Order)
		fmt.Printf(
			"Ascending: %t, Descending: %t\n",
			sort.Order == qparser.OrderAsc,
			sort.Order == qparser.OrderDesc,
		)
	}

Prints:

sort by "createdAt" "DESC" 
Ascending: false, Descending: true
sort by "title" "ASC" 
Ascending: true, Descending: false

Filters

For convenience QParser fills the filter list if the "filter" keyword is present in the query string with exactly 1 nested key which is interpreted as a field name. The value of this parameter is considered a predicate. The interpretation of the predicate must be implemented by the calling code. For example "filter[company]=eq:Velmie". If your filter implementation intends to use nested keys, like so "filter[company][eq]=Velmie", then use the "Values" directly.

	q := "filter[company]=eq:Velmie&filter[date]=notnull"

	query, _ := qparser.ParseQuery(q)

	filters, _ := json.MarshalIndent(query.Filters, "", "  ")
	fmt.Println(string(filters))

Prints:

[
  {
    "FieldName": "company",
    "Predicate": "eq:Velmie"
  },
  {
    "FieldName": "date",
    "Predicate": "notnull"
  }
]

Page

It is assumed that the page parameter will be used to implement pagination. QParser does not enforce a specific pagination implementation, therefore the "Page" structure contains the most popular terms: limit, offset; number, size; cursor. QParser fills the given structure with the corresponding values from page[limit], page[offset] etc.

	q := "page[size]=32&page[number]=8"

	query, _ := qparser.ParseQuery(q)

	page, _ := json.MarshalIndent(query.Page, "", "  ")
	fmt.Println(string(page))

Prints:

{
  "Size": "32",
  "Number": "8",
  "Limit": "",
  "Offset": "",
  "Cursor": ""
}

The "Request" structure

The Request structure can be useful when implementing API endpoints URLs following recommendations from the JSON:API specification. See the page, https://jsonapi.org/recommendations/#urls.

Owner
Velmie
Blockchain & Cryptocurrency Solutions.
Velmie
Get JSON values quickly - JSON parser for Go

get json values quickly GJSON is a Go package that provides a fast and simple way to get values from a json document. It has features such as one line

Josh Baker 10.3k May 19, 2022
A library to query the godoc.org JSON API.

gopkg This repository provides minimal Go package that makes queries against the godoc.org JSON API. Since that site has mostly been subsumed by pkg.g

M. J. Fromberger 2 Nov 26, 2021
Convert JSON string to Graphql

Convert JSON in string type (example below) to GraphQL Input in GolangConvert JSON in string type (example below) to GraphQL Input in Golang

Rizky Anggita S Siregar 0 Oct 5, 2021
A high-performance 100% compatible drop-in replacement of "encoding/json"

A high-performance 100% compatible drop-in replacement of "encoding/json" You can also use thrift like JSON using thrift-iterator Benchmark Source cod

Jsoniter 10.9k May 22, 2022
JSON query in Golang

gojq JSON query in Golang. Install go get -u github.com/elgs/gojq This library serves three purposes: makes parsing JSON configuration file much easie

Qian Chen 182 Apr 27, 2022
Console JSON formatter with query feature

Console JSON formatter with query feature. Install: $ go get github.com/miolini/jsonf Usage: Usage of jsonf: -c=true: colorize output -d=false: de

Artem Andreenko 63 Jan 23, 2022
A simple Go package to Query over JSON/YAML/XML/CSV Data

A simple Go package to Query over JSON Data. It provides simple, elegant and fast ODM like API to access, query JSON document Installation Install the

Saddam H 1.9k May 22, 2022
JSONata in Go Package jsonata is a query and transformation language for JSON

JSONata in Go Package jsonata is a query and transformation language for JSON. It's a Go port of the JavaScript library JSONata.

Blues Inc 24 Apr 27, 2022
Fast JSON parser and validator for Go. No custom structs, no code generation, no reflection

fastjson - fast JSON parser and validator for Go Features Fast. As usual, up to 15x faster than the standard encoding/json. See benchmarks. Parses arb

Aliaksandr Valialkin 1.5k May 11, 2022
A JSON stream parser for Go

pjson A JSON stream parser for Go Example The example below prints all string values from a JSON document. package

Josh Baker 59 Dec 2, 2021
One of the fastest alternative JSON parser for Go that does not require schema

Alternative JSON parser for Go (10x times faster standard library) It does not require you to know the structure of the payload (eg. create structs),

Leonid Bugaev 4.5k May 19, 2022
A fast json parser for go

rjson rjson is a json parser that relies on Ragel-generated state machines for most parsing. rjson's api is minimal and focussed on efficient parsing.

WillAbides 49 Feb 23, 2022
Slow and unreliable JSON parser generator (in progress)

VivaceJSON Fast and reliable JSON parser generator Todo List parse fields parse types generate struct generate (keypath+key) to struct Value Mapping F

null 7 Mar 3, 2022
Easy JSON parser for Go. No custom structs, no code generation, no reflection

Easy JSON parser for Go. No custom structs, no code generation, no reflection

null 2 Jan 4, 2022
A JSON parser/generator for Go

FASTJSON fastjson是java版本的fastjson库的api做一个翻译,方便习惯java的人操作json数据 主要适用场景:层级和字段都不能确定的json 这个库并不实现高性能json解析,依赖标准库json 这个库并没有100%实现java版的api 安装 go get -u gi

wuyunhua 2 Dec 29, 2021
JSON diff library for Go based on RFC6902 (JSON Patch)

jsondiff jsondiff is a Go package for computing the diff between two JSON documents as a series of RFC6902 (JSON Patch) operations, which is particula

William Poussier 166 May 7, 2022
Package json implements encoding and decoding of JSON as defined in RFC 7159

Package json implements encoding and decoding of JSON as defined in RFC 7159. The mapping between JSON and Go values is described in the documentation for the Marshal and Unmarshal functions

High Performance, Kubernetes Native Object Storage 3 May 10, 2022
Json-go - CLI to convert JSON to go and vice versa

Json To Go Struct CLI Install Go version 1.17 go install github.com/samit22/js

Samit Ghimire 5 Mar 3, 2022
JSON Spanner - A Go package that provides a fast and simple way to filter or transform a json document

JSON SPANNER JSON Spanner is a Go package that provides a fast and simple way to

null 2 May 20, 2022