Skip to content

Commit 13b756e

Browse files
committed
readme & delete wip
1 parent 5f815ed commit 13b756e

23 files changed

+485
-85
lines changed

GO_LICENSE

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2009 The Go Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of Google Inc. nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,181 @@
1-
# jsonpointer
1+
# jsonpointer - an [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) implementation for Go
2+
3+
Package jsonpointer provides the ability to resolve, assign, and delete values
4+
of any type, including raw JSON, by [JSON
5+
Pointers](https://datatracker.ietf.org/doc/html/rfc6901).
6+
7+
## Install
8+
9+
```bash
10+
go get github.com/chanced/jsonpointer
11+
```
12+
13+
## Usage
14+
15+
### General
16+
17+
```go
18+
package main
19+
20+
import (
21+
"log"
22+
"encoding/json"
23+
"github.com/chanced/jsonpointer"
24+
)
25+
26+
type Nested struct {
27+
Str string
28+
}
29+
30+
type Root struct {
31+
Nested Nested
32+
}
33+
34+
func main() {
35+
36+
r := Root{ Nested: Nested{ Str: "nested str" }}
37+
38+
// jsonpointer.JSONPointer is a string type so if you have a properly
39+
// formatted json pointer then you can simply convert it:
40+
// ptr := jsonpointer.JSONPointer(myPointer)
41+
// err := ptr.Validate()
42+
43+
// Note: jsonpointer.New encodes each token's value.
44+
// "/" encodes to "~1" and "~" encodes to "~0" in compliance with RFC 6901.
45+
46+
ptr := jsonpointer.New("nested", "str")
47+
48+
// Resolve
49+
50+
var s string
51+
err := jsonpointer.Resolve(r, ptr, &s)
52+
if err != nil {
53+
log.Fatal(err)
54+
}
55+
log.Println(s) // outputs "nested str"
56+
57+
// Assign
58+
59+
err = jsonpointer.Assign(&r, ptr, "new value")
60+
if err != nil {
61+
log.Fatal(err)
62+
}
63+
log.Println(r.Nested.Str) // outputs "new value"
64+
65+
66+
// Delete
67+
68+
err = jsonpointer.Delete(&r, ptr)
69+
if err != nil {
70+
log.Fatal(err)
71+
}
72+
log.Println(r.Nested.Str) // outputs ""
73+
74+
75+
// jsonpointer can also Resolve, Assign, and Delete JSON in []byte format.
76+
// This includes field values, such as those of type json.RawMessage.
77+
78+
r.Nested.Str = "str val"
79+
80+
b, err := json.Marshal(r)
81+
if err != nil {
82+
log.Fatal(err)
83+
}
84+
85+
err = jsonpointer.Resolve(b, ptr, &s)
86+
if err != nil {
87+
log.Fatal(err)
88+
}
89+
log.Println(s) // outputs "str val"
90+
}
91+
```
92+
93+
### Interfaces
94+
95+
Package jsonpointer provides 3 interfaces: `Assigner`, `Resolver`, and
96+
`Deleter`. Regardless of the operation, if `Resolver` is implemented, `ResolveJSONPointer` will be
97+
called. `ResolveJSONPointer` should not have side effects. If resolving for an assignment, utilize the
98+
pointer to infer which type should be assigned.
99+
100+
Both `AssignByJSONPointer` and `DeleteByJSONPointer` are invoked on the way back from the leaf.
101+
102+
All three methods are passed a pointer to the `jsonpointer.JSONPointer` so that
103+
it can be modified. If you do not modify it, jsonpointer will assume the current
104+
token was addressed and continue on.
105+
106+
If you wish to only handle some cases with the interfaces, return `jsonpointer.YieldOperation` to have the jsonpointer package resolve, assign, or delete as if the type did not implement the interface. Note that doing so results in changes to `ptr` being dismissed.
107+
108+
### JSONPointer methods
109+
110+
All methods return new values rather than modifying the pointer itself. If you wish to modify the pointer in one of the interface methods, you will need to reassign it: `*ptr = newPtrVal`
111+
112+
```go
113+
func (mt MyType) ResolveJSONPointer(ptr *jsonpointer.JSONPointer, op Operation) (interface{}, error) {
114+
next, t, ok := ptr.Next()
115+
if !ok {
116+
// this will only occur if the ptr is a root token
117+
return mt
118+
}
119+
if op == jsonpointer.Assigning && t == "someInterface" {
120+
// maybe you need to know what comes after someInterface to
121+
// determine what implementation of someInterface to assign
122+
t, _ = next.NextToken()
123+
124+
switch t {
125+
case "someIdentifier":
126+
// you could modify ptr if you felt so inclined: *ptr = next
127+
// but it is not needed in this scenario.
128+
return SomeImplementation{}, nil
129+
}
130+
}
131+
// otherwise hand resolution back over to jsonpointer
132+
return nil, jsonpointer.YieldOperation
133+
}
134+
```
135+
136+
## Errors
137+
138+
All errors returned from `Resolve`, `Assign`, and `Delete` will implement `Error`. A convenience method `AsError` exists to help extract out the details.
139+
140+
Depending on the cause, the error could also be `KeyError`, `IndexError`, `FieldError` with additional details. All have corresponding `As{Error}` methods.
141+
142+
Finally, all errors have associated Err instances that are wrapped, such as `ErrMalformedToken`, `ErrInvalidKeyType`, and so on.
143+
144+
See [errors.go for further details on errors](https://github.com/chanced/jsonpointer/blob/main/errors.go).
145+
146+
## Motivation
147+
148+
jsonpointer was built to support
149+
[github.com/chanced/openapi](https://github.com/chanced/openapi) but it may be
150+
useful for others so it has been released as an independent package.
151+
152+
## Contributions & Issues
153+
154+
Contributions are always welcome. If you run into an issue, please open a issue
155+
on github. If you would like to submit a change, feel free to open up a pull
156+
request.
157+
158+
## Note on Performance
159+
160+
This package is reflect heavy. While it employs the same caching mechanics as
161+
`encoding/json` to help alleviate some of the lookup costs, there will always be
162+
a performance hit with reflection.
163+
164+
There are also probably plenty of ways to improve performance of the package.
165+
Improvements or criticisms are always welcome.
166+
167+
With regards to raw JSON, `json.Marshal` and `json.Unmarshal` are utilized.
168+
Ideally, in the future, that will change and the package will incoroprate the
169+
encoding/decoding logic from `encoding/json` directly, thus skipping the need to
170+
run through unneccesary logic.
171+
172+
## Alternative JSON Pointer Packages for Go
173+
174+
- [github.com/dolmen-go/jsonptr](https://github.com/dolmen-go/jsonptr)
175+
- [github.com/qri-io/jsonpointer](https://github.com/qri-io/jsonpointer)
176+
- [github.com/xeipuuv/gojsonpointer](https://github.com/xeipuuv/gojsonpointer)
177+
- [github.com/go-openapi/jsonpointer](https://github.com/go-openapi/jsonpointer)
178+
179+
## License
180+
181+
[Apache 2.0](https://raw.githubusercontent.com/chanced/jsonpointer/main/LICENSE)

assign.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
// Copyright 2022 Chance Dinkins
2+
//
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+
//
6+
// The License can be found in the LICENSE file.
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
114
package jsonpointer
215

316
import (

assign_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
// Copyright 2022 Chance Dinkins
2+
//
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+
//
6+
// The License can be found in the LICENSE file.
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
114
package jsonpointer_test
215

316
import (

delete.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
1+
// Copyright 2022 Chance Dinkins
2+
//
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+
//
6+
// The License can be found in the LICENSE file.
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
114
package jsonpointer
215

16+
import "reflect"
17+
318
type Deleter interface {
419
DeleteByJSONPointer(ptr *JSONPointer) error
520
}
@@ -8,7 +23,27 @@ func Delete(src interface{}, ptr JSONPointer) error {
823
if err := ptr.Validate(); err != nil {
924
return err
1025
}
11-
s := newState(ptr, Deleting)
26+
dv := reflect.ValueOf(src)
27+
s := newState(ptr, Assigning)
1228
defer s.Release()
13-
panic("not done with Delete")
29+
if dv.Kind() != reflect.Ptr || dv.IsNil() {
30+
return &ptrError{
31+
state: *s,
32+
err: ErrNonPointer,
33+
typ: dv.Type(),
34+
}
35+
}
36+
cpy := dv
37+
dv = dv.Elem()
38+
if dv.Kind() == reflect.Ptr && dv.IsNil() {
39+
dv = reflect.New(dv.Type().Elem())
40+
}
41+
dp := reflect.New(dv.Type())
42+
dp.Elem().Set(dv)
43+
res, err := s.delete(dp)
44+
if err != nil {
45+
return err
46+
}
47+
cpy.Elem().Set(res.Elem())
48+
return nil
1449
}

delete_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
// Copyright 2022 Chance Dinkins
2+
//
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+
//
6+
// The License can be found in the LICENSE file.
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
114
package jsonpointer_test
215

316
import (

errors.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
// Copyright 2022 Chance Dinkins
2+
//
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+
//
6+
// The License can be found in the LICENSE file.
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
114
package jsonpointer
215

316
import (

examples/main.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package main
2+
3+
import (
4+
"log"
5+
6+
"github.com/chanced/jsonpointer"
7+
)
8+
9+
type Nested struct {
10+
Str string
11+
}
12+
13+
type Root struct {
14+
Nested Nested
15+
}
16+
17+
func main() {
18+
r := Root{Nested: Nested{Str: "nested str"}}
19+
20+
// JSONPointer is a string so if you have a properly formatted pointer,
21+
// simply convert it:
22+
// ptr := jsonpointer.JSONPointer(myPointer)
23+
24+
ptr := jsonpointer.New("nested", "str")
25+
// Note: jsonpointer.New does not validate and it encodes each token's value.
26+
// which means "/" is encoded to "~1" and "~" encodes to "~0" in compliance
27+
// with RFC 6901.
28+
29+
// Resolve
30+
31+
var s string
32+
33+
err := jsonpointer.Resolve(r, ptr, &s)
34+
if err != nil {
35+
log.Fatal(err)
36+
}
37+
log.Println(s) // outputs "nested str"
38+
39+
// Assign
40+
41+
err = jsonpointer.Assign(&r, ptr, "new value")
42+
if err != nil {
43+
log.Fatal(err)
44+
}
45+
log.Println(r.Nested.Str) // outputs "new value"
46+
47+
// Delete
48+
49+
err = jsonpointer.Delete(&r, ptr)
50+
if err != nil {
51+
log.Fatal(err)
52+
}
53+
log.Println(r.Nested.Str) // outputs ""
54+
}

fold.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright 2010 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the GO_LICENSE file.
4+
15
package jsonpointer
26

37
import (

0 commit comments

Comments
 (0)