Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dotnet compatibility #11

Merged
merged 4 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 8 additions & 26 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,64 +15,46 @@ env:
jobs:
test_cover:
name: Coverage
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04

env:
CGO_ENABLED: 0
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: 1.21

- name: Restore Go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}

- name: Update Go modules
run: go mod download -json

- name: Write coverage profile
run: go test -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...

- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: false
path_to_write_report: ./coverage.txt
verbose: true

tests:
name: Go
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
strategy:
matrix:
go_versions: [ '1.19', '1.20' ]
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: '${{ matrix.go_versions }}'

- name: Restore Go modules from cache
uses: actions/cache@v2
with:
path: /home/runner/go/pkg/mod
key: deps-${{ hashFiles('go.sum') }}

- name: Update Go modules
run: go mod download -json

- name: Run tests
run: go test -v -race ./...
run: go test -v -race ./...
41 changes: 12 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
nspcc-dev fork of go-ordered-json
===============
.NET-compatible JSON library
============================

There are some legacy/stupid applications[1] that you need to interoperate with,
and they for whatever reason require that the JSON you're using is ordered in
a particular way (contrary to the JSON specifications).
It's made for 100% compatibility with the JSON variation used by
[Neo blockchain](https://github.com/neo-project/). There are three problems
there:
* it's ordered (that's why it's a fork of [go-ordered-json](https://github.com/virtuald/go-ordered-json))
* it has different conventions regarding control and "special" symbols
* it has different conventions wrt incorrect UTF-8

Unfortunately, the golang authors are not willing to support such a broken use
case, so on [their advice](https://groups.google.com/forum/#!topic/golang-dev/zBQwhm3VfvU)
this is a fork of the golang encoding/json package, with the ordered JSON
support originating with a patch from
[Peter Waldschmidt](https://go-review.googlesource.com/c/7930/).
The primary user of this library is [NeoGo](https://github.com/nspcc-dev/neo-go/),
it has to be 100% compatible with C# implementation to correctly process
transactions, that's why we're maintaining this library and solving any
inconsistencies with .NET libraries if found.

**If you can, you should avoid using this package**. However, if you can't
avoid it, then you are welcome to. Provided under the MIT license, just like
golang.

Known broken applications
-------------------------

* [1][Windows Communication Foundation Json __type ordering](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/stand-alone-json-serialization#type-hint-position-in-json-objects)
* [2][NEO node](https://github.com/neo-project/neo/tree/master/src/neo/IO/Json)

Fork motivations
-------------------------

NEO project has its own implementation of JSON serializer which is more a JSON
dialect rather than standard-compatible implementation. Until JSON serialisation
format affects contract states we need to be byte-to-byte compatible with the
reference JSON serializer. This fork contains the following compatibility quirks:

* JSON serializer is ordered (see https://github.com/nspcc-dev/neo-go/pull/2026) (implemented in the original [virtuald/go-ordered-json](https://github.com/virtuald/go-ordered-json) repository)
* JSON serializer escapes non-ascii characters while marshalling (see https://github.com/nspcc-dev/neo-go/pull/2174)

More compatibility quirks may be added in the future.
7 changes: 3 additions & 4 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,13 @@ import (
//
// The JSON null value unmarshals into an interface, map, pointer, or slice
// by setting that Go value to nil. Because null is often used in JSON to mean
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
// not present, unmarshaling a JSON null into any other Go type has no effect
// on the value and produces no error.
//
// When unmarshaling quoted strings, invalid UTF-8 or
// invalid UTF-16 surrogate pairs are not treated as an error.
// Instead, they are replaced by the Unicode replacement
// character U+FFFD.
//
func Unmarshal(data []byte, v interface{}) error {
// Check for well-formedness.
// Avoids filling out half a data structure
Expand Down Expand Up @@ -274,8 +273,8 @@ type decodeState struct {
Struct string
Field string
}
savedError error
useNumber bool
savedError error
useNumber bool
useOrderedObject bool
}

Expand Down
22 changes: 11 additions & 11 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,12 @@ func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error {
}

type unmarshalTest struct {
in string
ptr interface{}
out interface{}
err error
useNumber bool
golden bool
in string
ptr interface{}
out interface{}
err error
useNumber bool
golden bool
useOrderedObject bool
}

Expand Down Expand Up @@ -858,12 +858,12 @@ func TestMarshal(t *testing.T) {
var badUTF8 = []struct {
in, out string
}{
{"hello\xffworld", `"hello\ufffdworld"`},
{"hello\xffworld", `"hello\u00FFworld"`},
{"", `""`},
{"\xff", `"\ufffd"`},
{"\xff\xff", `"\ufffd\ufffd"`},
{"a\xffb", `"a\ufffdb"`},
{"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"\u65E5\u672C\ufffd\ufffd\ufffd"`},
{"\xff", `"\u00FF"`},
{"\xff\xff", `"\u00FF\u00FF"`},
{"a\xffb", `"a\u00FFb"`},
{"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"\u65E5\u672C\u00FF\u00AA\u009E"`},
}

func TestMarshalBadUTF8(t *testing.T) {
Expand Down
51 changes: 33 additions & 18 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,31 +81,31 @@ import (
//
// Examples of struct field tags and their meanings:
//
// // Field appears in JSON as key "myName".
// Field int `json:"myName"`
// // Field appears in JSON as key "myName".
// Field int `json:"myName"`
//
// // Field appears in JSON as key "myName" and
// // the field is omitted from the object if its value is empty,
// // as defined above.
// Field int `json:"myName,omitempty"`
// // Field appears in JSON as key "myName" and
// // the field is omitted from the object if its value is empty,
// // as defined above.
// Field int `json:"myName,omitempty"`
//
// // Field appears in JSON as key "Field" (the default), but
// // the field is skipped if empty.
// // Note the leading comma.
// Field int `json:",omitempty"`
// // Field appears in JSON as key "Field" (the default), but
// // the field is skipped if empty.
// // Note the leading comma.
// Field int `json:",omitempty"`
//
// // Field is ignored by this package.
// Field int `json:"-"`
// // Field is ignored by this package.
// Field int `json:"-"`
//
// // Field appears in JSON as key "-".
// Field int `json:"-,"`
// // Field appears in JSON as key "-".
// Field int `json:"-,"`
//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
// when communicating with JavaScript programs:
//
// Int64String int64 `json:",string"`
// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, and ASCII punctuation except quotation
Expand Down Expand Up @@ -161,7 +161,6 @@ import (
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(v interface{}) ([]byte, error) {
e := &encodeState{}
err := e.marshal(v, encOpts{escapeHTML: true})
Expand Down Expand Up @@ -917,9 +916,15 @@ func (e *encodeState) string(s string, escapeHTML bool) int {
case '\\':
e.WriteByte('\\')
e.WriteByte(b)
case 0x08:
e.WriteByte('\\')
e.WriteByte('b')
case '\n':
e.WriteByte('\\')
e.WriteByte('n')
case 0x0c:
e.WriteByte('\\')
e.WriteByte('f')
case '\r':
e.WriteByte('\\')
e.WriteByte('r')
Expand All @@ -945,7 +950,9 @@ func (e *encodeState) string(s string, escapeHTML bool) int {
if start < i {
e.WriteString(s[start:i])
}
e.WriteString(`\ufffd`)
e.WriteString(`\u00`)
e.WriteByte(hex[s[i]>>4])
e.WriteByte(hex[s[i]&0xF])
i += size
start = i
continue
Expand Down Expand Up @@ -1004,9 +1011,15 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) int {
case '\\':
e.WriteByte('\\')
e.WriteByte(b)
case 0x08:
e.WriteByte('\\')
e.WriteByte('b')
case '\n':
e.WriteByte('\\')
e.WriteByte('n')
case 0x0c:
e.WriteByte('\\')
e.WriteByte('f')
case '\r':
e.WriteByte('\\')
e.WriteByte('r')
Expand All @@ -1032,7 +1045,9 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) int {
if start < i {
e.Write(s[start:i])
}
e.WriteString(`\ufffd`)
e.WriteString(`\u00`)
e.WriteByte(hex[s[i]>>4])
e.WriteByte(hex[s[i]&0xF])
i += size
start = i
continue
Expand Down
45 changes: 10 additions & 35 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ func (CText) MarshalText() ([]byte, error) {
return []byte(`"<&>"`), nil
}


func TestMarshaler_NeoGo_PR2174(t *testing.T) {
source := "IOU(欠条币):一种支持负数的NEP-17(非严格意义上的)资产,合约无存储区,账户由区块链浏览器统计"
b, err := Marshal(source)
Expand Down Expand Up @@ -578,40 +577,16 @@ var encodeStringTests = []struct {
in string
out string
}{
{"\x00", `"\u0000"`},
{"\x01", `"\u0001"`},
{"\x02", `"\u0002"`},
{"\x03", `"\u0003"`},
{"\x04", `"\u0004"`},
{"\x05", `"\u0005"`},
{"\x06", `"\u0006"`},
{"\x07", `"\u0007"`},
{"\x08", `"\u0008"`},
{"\x09", `"\t"`},
{"\x0a", `"\n"`},
{"\x0b", `"\u000B"`},
{"\x0c", `"\u000C"`},
{"\x0d", `"\r"`},
{"\x0e", `"\u000E"`},
{"\x0f", `"\u000F"`},
{"\x10", `"\u0010"`},
{"\x11", `"\u0011"`},
{"\x12", `"\u0012"`},
{"\x13", `"\u0013"`},
{"\x14", `"\u0014"`},
{"\x15", `"\u0015"`},
{"\x16", `"\u0016"`},
{"\x17", `"\u0017"`},
{"\x18", `"\u0018"`},
{"\x19", `"\u0019"`},
{"\x1a", `"\u001A"`},
{"\x1b", `"\u001B"`},
{"\x1c", `"\u001C"`},
{"\x1d", `"\u001D"`},
{"\x1e", `"\u001E"`},
{"\x1f", `"\u001F"`},
{"'", `"\u0027"`},
{"\"", `"\u0022"`},
{"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", `"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"`},
{"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f", `" !\u0022#$%\u0026\u0027()*\u002B,-./0123456789:;\u003C=\u003E?"`},
{"\x40\x41\x44\x45\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x54\x55\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f", `"@ADEDEFGHIJKLMNOPQTUTUVWXYZ[\\]^_"`},
{"\x60\x61\x66\x67\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x76\x77\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f", `"\u0060afgdefghijklmnopqvwtuvwxyz{|}~\u007F"`},
{"\x80\x81\x88\x89\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x98\x99\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", `"\u0080\u0081\u0088\u0089\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0098\u0099\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F"`},
{"\xa0\xa1\xaa\xab\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xba\xbb\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf", `"\u00A0\u00A1\u00AA\u00AB\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00BA\u00BB\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF"`},
{"\xc0\xc1\xcc\xcd\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xdc\xdd\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf", `"\u00C0\u00C1\u00CC\u00CD\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00DC\u00DD\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF"`},
{"\xe0\xe1\xee\xef\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xfe\xff\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", `"\u00E0\u00E1\u00EE\u00EF\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00FE\u00FF\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF"`},
{"\xff\xd0", `"\u00FF\u00D0"`},
{"测试", `"\u6D4B\u8BD5"`},
}

func TestEncodeString(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ package json_test

import (
"bytes"
"github.com/virtuald/go-ordered-json"
"fmt"
"github.com/virtuald/go-ordered-json"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

"io"
"log"
"os"
Expand Down Expand Up @@ -312,4 +312,4 @@ func ExampleOrderedObject() {
// name=André-Marie Ampère born=1777 died=1836
// Encoded:
// {"name":"Hans Christian Ørsted","born":1777,"died":1851,"nationality":"Danish"}
}
}
5 changes: 3 additions & 2 deletions fold.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ const (
// 4) simpleLetterEqualFold, no specials, no non-letters.
//
// The letters S and K are special because they map to 3 runes, not just 2:
// * S maps to s and to U+017F 'ſ' Latin small letter long s
// * k maps to K and to U+212A 'K' Kelvin sign
// - S maps to s and to U+017F 'ſ' Latin small letter long s
// - k maps to K and to U+212A 'K' Kelvin sign
//
// See https://play.golang.org/p/tTxjOc0OGo
//
// The returned function is specialized for matching against s and
Expand Down
Loading
Loading