json-iterator / go

A high-performance 100% compatible drop-in replacement of "encoding/json"
http://jsoniter.com/migrate-from-go-std.html
MIT License
13.43k stars 1.03k forks source link

Incorrect indention when marshaling map/object #564

Open creekwax opened 3 years ago

creekwax commented 3 years ago

There are some cases jsoniter behaves incorrectly when marshaling map/object with indention.

  1. Nested map/obj/slice inside map encoding using sortedKeysMapEncoder have incorrect indentions.
    func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
    // ...
    subStream := stream.cfg.BorrowStream(nil)
    subStream.Attachment = stream.Attachment
    // ...
    stream.cfg.ReturnStream(subStream)
    }

    The stream borrowed from sync.Pool always has 0 initial indention and returned with 0 indention. So when processing nested elements if SortMapKeys: true is configured inside the Config, the encoding will always have a single unit of indention step. eg.

    
    var json = jsoniter.ConfigCompatibleWithStandardLibrary
    var obj interface{}
    var x []byte

obj = map[int]map[int]int{1: {1: 2}} x, _ = json.MarshalIndent(obj, "", " ") fmt.Println(string(x))

obj = map[int]map[int]int{1: {1: 2}} x, _ = json.MarshalIndent(obj, "", " ") fmt.Println(string(x))

obj = map[int]struct{ F int }{1: {F: 2}} x, _ = json.MarshalIndent(obj, "", " ") fmt.Println(string(x))

Results:
```json
{
    "1": [
    1,
    2
]
}
{
    "1": {
    "1": 2
}
}
{
    "1": {
    "F": 2
}
}

Expects:

{
    "1": [
        1,
        2
    ]
}
{
    "1": {
        "1": 2
    }
}
{
    "1": {
        "F": 2
    }
}
  1. Empty map or object with no fields to be marshaled has redundant indention introduced by (*Stream).WriteObjectStart.
    • Empty map is not recognized as an empty object to be written by a Stream.
    • Object that all fields with omitempty tag will have redundant indention if these exported fields are all empty.
      
      var json = jsoniter.ConfigCompatibleWithStandardLibrary
      var obj interface{}
      var x []byte

obj = map[int]int{} x, _ = json.MarshalIndent(obj, "", " ") fmt.Println(string(x))

obj = struct { Empty int json:"empty,omitempty" }{} x, _ = json.MarshalIndent(obj, "", " ") fmt.Println(string(x))

Results:
```json
{

}
{

}

Expects:

{}
{}