Files
gf/encoding/gjson/gjson_z_unit_feature_load_test.go
John Guo f3f2cb3c57 refactor(encoding/gjson): enhance auto type checks when loading data without type specified (#4637)
This pull request improves YAML support for i18n translation files and
refactors content type detection and loading logic in the `gjson`
package. The main changes include more robust detection of YAML, TOML,
INI, and Properties formats, refactoring of content type handling, and
the addition of new tests to ensure correct parsing of YAML-based i18n
resources.

### Improved content type detection and loading

* Refactored content type detection logic in `gjson` to use dedicated
functions for XML, YAML, TOML, INI, and Properties formats, making the
detection more reliable and maintainable.
* Changed the content loading mechanism in `gjson` to use specific
decode functions (`gxml.Decode`, `gyaml.Decode`, etc.) for each format
instead of converting everything to JSON first, improving accuracy and
extensibility.
* Updated type definitions and struct field comments in `gjson.go` for
clarity and consistency, including changing `ContentType` to a type
alias and improving documentation.
[[1]](diffhunk://#diff-0e4432d7e4cf171c0339e01b1842530432b986948d7f839a155543623236a03fL24-R24)
[[2]](diffhunk://#diff-0e4432d7e4cf171c0339e01b1842530432b986948d7f839a155543623236a03fL38-R71)

### i18n YAML support

* Modified i18n manager to use the new `gjson.LoadPath` method for
loading translation files, ensuring correct parsing of YAML files for
i18n.
* Added new test cases and test data for loading and verifying YAML i18n
files, including edge cases and real-world translation strings.
[[1]](diffhunk://#diff-e6eacc5abab33c149f9b39d8ebe300cf4d0abe907434605991984a5969e8707dR262-R283)
[[2]](diffhunk://#diff-1bfd438797c1f9ef18ab3cb00d23ae95202e85e2362c39c3df4f1a29c55733feR421-R430)
[[3]](diffhunk://#diff-a3ee37ff2a67c9e1ba2e1617e0f5fd63eb261ad7760a07423f703538138c2decR1-R16)

### Minor improvements

* Simplified file loading logic in `gjson.LoadPath` by removing caching
and directly reading file bytes, which streamlines the code and avoids
potential cache issues.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-20 19:25:23 +08:00

431 lines
11 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gjson_test
import (
"testing"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Load_JSON1(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{1, 2, 3})
t.Assert(j.Get("a.1").Int(), 2)
})
// JSON
gtest.C(t, func(t *gtest.T) {
errData := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]`)
_, err := gjson.LoadContentType("json", errData, true)
t.AssertNE(err, nil)
})
// JSON
gtest.C(t, func(t *gtest.T) {
path := "test.json"
gfile.PutBytes(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path, true)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{1, 2, 3})
t.Assert(j.Get("a.1").Int(), 2)
})
}
func Test_Load_JSON2(t *testing.T) {
data := []byte(`{"n":123456789000000000000, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789000000000000")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{1, 2, 3})
t.Assert(j.Get("a.1").Int(), 2)
})
}
func Test_Load_XML(t *testing.T) {
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("doc.n").String(), "123456789")
t.Assert(j.Get("doc.m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("doc.m.k").String(), "v")
t.Assert(j.Get("doc.a").Slice(), g.Slice{"1", "2", "3"})
t.Assert(j.Get("doc.a.1").Int(), 2)
})
// XML
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadXml(data, true)
t.AssertNil(err)
t.Assert(j.Get("doc.n").String(), "123456789")
t.Assert(j.Get("doc.m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("doc.m.k").String(), "v")
t.Assert(j.Get("doc.a").Slice(), g.Slice{"1", "2", "3"})
t.Assert(j.Get("doc.a.1").Int(), 2)
})
// XML
gtest.C(t, func(t *gtest.T) {
errData := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n><doc>`)
_, err := gjson.LoadContentType("xml", errData, true)
t.AssertNE(err, nil)
})
// XML
gtest.C(t, func(t *gtest.T) {
path := "test.xml"
gfile.PutBytes(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
t.AssertNil(err)
t.Assert(j.Get("doc.n").String(), "123456789")
t.Assert(j.Get("doc.m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("doc.m.k").String(), "v")
t.Assert(j.Get("doc.a").Array(), g.Slice{"1", "2", "3"})
t.Assert(j.Get("doc.a.1").Int(), 2)
})
// XML
gtest.C(t, func(t *gtest.T) {
xml := []byte(`
<?xml version="1.0"?>
<Output type="o">
<itotalSize>0</itotalSize>
<ipageSize>1</ipageSize>
<ipageIndex>2</ipageIndex>
<itotalRecords>GF框架</itotalRecords>
<nworkOrderDtos/>
<nworkOrderFrontXML/>
</Output>
`)
j, err := gjson.LoadContent(xml)
t.AssertNil(err)
t.Assert(j.Get("Output.ipageIndex"), "2")
t.Assert(j.Get("Output.itotalRecords"), "GF框架")
})
}
func Test_Load_YAML1(t *testing.T) {
data := []byte(`
a:
- 1
- 2
- 3
m:
k: v
"n": 123456789
`)
// YAML
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{1, 2, 3})
t.Assert(j.Get("a.1").Int(), 2)
})
// YAML
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadYaml(data, true)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{1, 2, 3})
t.Assert(j.Get("a.1").Int(), 2)
})
// YAML
gtest.C(t, func(t *gtest.T) {
path := "test.yaml"
gfile.PutBytes(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{1, 2, 3})
t.Assert(j.Get("a.1").Int(), 2)
})
}
func Test_Load_YAML2(t *testing.T) {
data := []byte("i : 123456789")
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("i"), "123456789")
})
gtest.C(t, func(t *gtest.T) {
errData := []byte("i # 123456789")
_, err := gjson.LoadContentType("yaml", errData, true)
t.AssertNE(err, nil)
})
}
func Test_Load_TOML1(t *testing.T) {
data := []byte(`
a = ["1", "2", "3"]
n = 123456789
[m]
k = "v"
`)
// TOML
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{"1", "2", "3"})
t.Assert(j.Get("a.1").Int(), 2)
})
// TOML
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadToml(data, true)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{"1", "2", "3"})
t.Assert(j.Get("a.1").Int(), 2)
})
// TOML
gtest.C(t, func(t *gtest.T) {
path := "test.toml"
gfile.PutBytes(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
t.AssertNil(err)
t.Assert(j.Get("n").String(), "123456789")
t.Assert(j.Get("m").Map(), g.Map{"k": "v"})
t.Assert(j.Get("m.k").String(), "v")
t.Assert(j.Get("a").Slice(), g.Slice{"1", "2", "3"})
t.Assert(j.Get("a.1").Int(), 2)
})
}
func Test_Load_TOML2(t *testing.T) {
data := []byte("i=123456789")
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
t.Assert(j.Get("i"), "123456789")
})
gtest.C(t, func(t *gtest.T) {
errData := []byte("i : 123456789")
_, err := gjson.LoadContentType("toml", errData, true)
t.AssertNE(err, nil)
})
}
func Test_Load_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
j := gjson.New(nil)
t.Assert(j.Interface(), nil)
_, err := gjson.Decode(nil)
t.AssertNE(err, nil)
_, err = gjson.DecodeToJson(nil)
t.AssertNE(err, nil)
j, err = gjson.LoadContent(nil)
t.AssertNil(err)
t.Assert(j.Interface(), nil)
j, err = gjson.LoadContent([]byte(`{"name": "gf"}`))
t.AssertNil(err)
j, err = gjson.LoadContent([]byte(`{"name": "gf"""}`))
t.AssertNE(err, nil)
j = gjson.New(&g.Map{"name": "gf"})
t.Assert(j.Get("name").String(), "gf")
})
}
func Test_Load_Ini(t *testing.T) {
var data = []byte(`
;注释
[addr]
ip = 127.0.0.1
port=9001
enable=true
[DBINFO]
type=mysql
user=root
password=password
`)
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
if err != nil {
gtest.Fatal(err)
}
t.Assert(j.Get("addr.ip").String(), "127.0.0.1")
t.Assert(j.Get("addr.port").String(), "9001")
t.Assert(j.Get("addr.enable").String(), "true")
t.Assert(j.Get("DBINFO.type").String(), "mysql")
t.Assert(j.Get("DBINFO.user").String(), "root")
t.Assert(j.Get("DBINFO.password").String(), "password")
_, err = j.ToIni()
if err != nil {
gtest.Fatal(err)
}
})
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadIni(data, true)
if err != nil {
gtest.Fatal(err)
}
t.Assert(j.Get("addr.ip").String(), "127.0.0.1")
t.Assert(j.Get("addr.port").String(), "9001")
t.Assert(j.Get("addr.enable").String(), "true")
t.Assert(j.Get("DBINFO.type").String(), "mysql")
t.Assert(j.Get("DBINFO.user").String(), "root")
t.Assert(j.Get("DBINFO.password").String(), "password")
})
gtest.C(t, func(t *gtest.T) {
errData := []byte("i : 123456789")
_, err := gjson.LoadContentType("ini", errData, true)
t.AssertNE(err, nil)
})
}
func Test_Load_YamlWithV3(t *testing.T) {
content := []byte(`
# CLI tool, only in development environment.
# https://goframe.org/pages/viewpage.action?pageId=3673173
gfcli:
gen:
dao:
- path : "../../pkg/oss/oss/internal"
group : "oss"
stdTime : true
descriptionTag : true
noJsonTag : true
noModelComment : true
overwriteDao : true
modelFileForDao : "model_dao.go"
tablesEx : |
bpmn_info,
dlocker,
dlocker_detail,
message_table,
monitor_data,
resource_param_info,
version_info,
version_topology_info,
work_flow,
work_flow_step_info,
work_flow_undo_step_info
- path : "../../pkg/oss/workflow/internal"
group : "workflow"
stdTime : true
descriptionTag : true
noJsonTag : true
noModelComment : true
overwriteDao : true
modelFileForDao : "model_dao.go"
`)
gtest.C(t, func(t *gtest.T) {
_, err := gjson.LoadContent(content)
t.AssertNil(err)
})
}
func Test_Load_Properties(t *testing.T) {
var data = []byte(`
#注释
addr.ip = 127.0.0.1
addr.port=9001
addr.enable=true
DBINFO.type=mysql
DBINFO.user=root
DBINFO.password=password
`)
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
if err != nil {
gtest.Fatal(err)
}
t.Assert(j.Get("addr.ip").String(), "127.0.0.1")
t.Assert(j.Get("addr.port").String(), "9001")
t.Assert(j.Get("addr.enable").String(), "true")
t.Assert(j.Get("DBINFO.type").String(), "mysql")
t.Assert(j.Get("DBINFO.user").String(), "root")
t.Assert(j.Get("DBINFO.password").String(), "password")
_, err = j.ToProperties()
if err != nil {
gtest.Fatal(err)
}
})
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadProperties(data, true)
if err != nil {
gtest.Fatal(err)
}
t.Assert(j.Get("addr.ip").String(), "127.0.0.1")
t.Assert(j.Get("addr.port").String(), "9001")
t.Assert(j.Get("addr.enable").String(), "true")
t.Assert(j.Get("DBINFO.type").String(), "mysql")
t.Assert(j.Get("DBINFO.user").String(), "root")
t.Assert(j.Get("DBINFO.password").String(), "password")
})
gtest.C(t, func(t *gtest.T) {
errData := []byte("i\\u1 : 123456789")
_, err := gjson.LoadContentType("properties", errData, true)
t.AssertNE(err, nil)
})
}
func Test_Load_YAML_For_I18n(t *testing.T) {
var data = []byte(gtest.DataContent("yaml", "i18n-issue.yaml"))
gtest.C(t, func(t *gtest.T) {
j, err := gjson.LoadContent(data)
t.AssertNil(err)
j.SetViolenceCheck(true)
t.Assert(j.Get("resourceUsage.workflow").String(), "workflow")
})
}