blob: bee7536760d5f2f7de8ea41a605b413d933fc678 [file] [log] [blame]
// @author sigu-399
// @description An implementation of JSON Pointer - Go language
// @created 25-02-2013
package gojsonpointer
import (
"errors"
"fmt"
"reflect"
"strings"
)
const (
EMPTY_POINTER = ``
POINTER_SEPARATOR = `/`
FRAGMENT = `#`
)
const (
INVALID_START = `JSON pointer must be empty, start with a "` + POINTER_SEPARATOR + `" or a "` + FRAGMENT + `"`
)
func NewJsonPointer(jsonPointerString string) (JsonPointer, error) {
var p JsonPointer
err := p.parse(jsonPointerString)
return p, err
}
type JsonPointer struct {
hasFragment bool
referenceTokens []string
}
func (p *JsonPointer) parse(jsonPointerString string) error {
var err error
if jsonPointerString != EMPTY_POINTER {
p.hasFragment = strings.HasPrefix(jsonPointerString, FRAGMENT)
if !strings.HasPrefix(jsonPointerString, POINTER_SEPARATOR) && !p.hasFragment {
err = errors.New(INVALID_START)
} else {
referenceTokens := strings.Split(jsonPointerString, POINTER_SEPARATOR)
for _, referenceToken := range referenceTokens[1:] {
p.referenceTokens = append(p.referenceTokens, decodeReferenceToken(referenceToken))
}
}
}
return err
}
func (p *JsonPointer) Get(document interface{}) (interface{}, error) {
if len(p.referenceTokens) == 0 {
return document, nil
}
node := document
for _, token := range p.referenceTokens {
rValue := reflect.ValueOf(node)
kind := rValue.Kind()
switch kind {
case reflect.Map:
m := node.(map[string]interface{})
if _, ok := m[token]; ok {
node = m[token]
} else {
return nil, errors.New("No value found using this pointer")
}
default:
return nil, errors.New(fmt.Sprintf("Unhandled kind %s in JsonPointer.Get", kind))
}
}
return node, nil
}
func (p *JsonPointer) String() string {
tokens := p.referenceTokens
for i := range tokens {
tokens[i] = encodeReferenceToken(tokens[i])
}
pointerString := strings.Join(tokens, POINTER_SEPARATOR)
if p.hasFragment {
return FRAGMENT + POINTER_SEPARATOR + pointerString
}
return pointerString
}
const (
ENCODED_REFERENCE_TOKEN_0 = `~0`
ENCODED_REFERENCE_TOKEN_1 = `~1`
DECODED_REFERENCE_TOKEN_0 = `~`
DECODED_REFERENCE_TOKEN_1 = `/`
)
func decodeReferenceToken(token string) string {
step1 := strings.Replace(token, ENCODED_REFERENCE_TOKEN_1, DECODED_REFERENCE_TOKEN_1, -1)
step2 := strings.Replace(step1, ENCODED_REFERENCE_TOKEN_0, DECODED_REFERENCE_TOKEN_0, -1)
return step2
}
func encodeReferenceToken(token string) string {
step1 := strings.Replace(token, DECODED_REFERENCE_TOKEN_1, ENCODED_REFERENCE_TOKEN_1, -1)
step2 := strings.Replace(step1, DECODED_REFERENCE_TOKEN_0, ENCODED_REFERENCE_TOKEN_0, -1)
return step2
}