1
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
16
// author-github https://github.com/xeipuuv
17
// author-mail xeipuuv@gmail.com
19
// repository-name gojsonpointer
20
// repository-desc An implementation of JSON Pointer - Go language
22
// description Main and unique file.
37
const_empty_pointer = ``
38
const_pointer_separator = `/`
40
const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator
43
type implStruct struct {
44
mode string // "SET" or "GET"
46
inDocument interface{}
48
setInValue interface{}
50
getOutNode interface{}
51
getOutKind reflect.Kind
55
func NewJsonPointer(jsonPointerString string) (JsonPointer, error) {
58
err := p.parse(jsonPointerString)
63
type JsonPointer struct {
64
referenceTokens []string
67
// "Constructor", parses the given string JSON pointer
68
func (p *JsonPointer) parse(jsonPointerString string) error {
72
if jsonPointerString != const_empty_pointer {
73
if !strings.HasPrefix(jsonPointerString, const_pointer_separator) {
74
err = errors.New(const_invalid_start)
76
referenceTokens := strings.Split(jsonPointerString, const_pointer_separator)
77
for _, referenceToken := range referenceTokens[1:] {
78
p.referenceTokens = append(p.referenceTokens, referenceToken)
86
// Uses the pointer to retrieve a value from a JSON document
87
func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
89
is := &implStruct{mode: "GET", inDocument: document}
91
return is.getOutNode, is.getOutKind, is.outError
95
// Uses the pointer to update a value from a JSON document
96
func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
98
is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
100
return document, is.outError
104
// Both Get and Set functions use the same implementation to avoid code duplication
105
func (p *JsonPointer) implementation(i *implStruct) {
107
kind := reflect.Invalid
109
// Full document when empty
110
if len(p.referenceTokens) == 0 {
111
i.getOutNode = i.inDocument
120
for ti, token := range p.referenceTokens {
122
decodedToken := decodeReferenceToken(token)
123
isLastToken := ti == len(p.referenceTokens)-1
125
rValue := reflect.ValueOf(node)
131
m := node.(map[string]interface{})
132
if _, ok := m[decodedToken]; ok {
133
node = m[decodedToken]
134
if isLastToken && i.mode == "SET" {
135
m[decodedToken] = i.setInValue
138
i.outError = errors.New(fmt.Sprintf("Object has no key '%s'", token))
145
s := node.([]interface{})
146
tokenIndex, err := strconv.Atoi(token)
148
i.outError = errors.New(fmt.Sprintf("Invalid array index '%s'", token))
154
if tokenIndex < 0 || tokenIndex >= sLength {
155
i.outError = errors.New(fmt.Sprintf("Out of bound array[0,%d] index '%d'", sLength, tokenIndex))
162
if isLastToken && i.mode == "SET" {
163
s[tokenIndex] = i.setInValue
167
i.outError = errors.New(fmt.Sprintf("Invalid token reference '%s'", token))
175
rValue := reflect.ValueOf(node)
183
// Pointer to string representation function
184
func (p *JsonPointer) String() string {
186
if len(p.referenceTokens) == 0 {
187
return const_empty_pointer
190
pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
195
// Specific JSON pointer encoding here
198
// ... and vice versa
201
const_encoded_reference_token_0 = `~0`
202
const_encoded_reference_token_1 = `~1`
203
const_decoded_reference_token_0 = `~`
204
const_decoded_reference_token_1 = `/`
207
func decodeReferenceToken(token string) string {
208
step1 := strings.Replace(token, const_encoded_reference_token_1, const_decoded_reference_token_1, -1)
209
step2 := strings.Replace(step1, const_encoded_reference_token_0, const_decoded_reference_token_0, -1)
213
func encodeReferenceToken(token string) string {
214
step1 := strings.Replace(token, const_decoded_reference_token_1, const_encoded_reference_token_1, -1)
215
step2 := strings.Replace(step1, const_decoded_reference_token_0, const_encoded_reference_token_0, -1)