Skip to content

Commit

Permalink
添加基本功能
Browse files Browse the repository at this point in the history
  • Loading branch information
yangyile committed Dec 24, 2024
1 parent f6fbc6c commit 3dc0458
Show file tree
Hide file tree
Showing 9 changed files with 549 additions and 2 deletions.
75 changes: 75 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: create-release

on:
push:
branches:
- main # 监听 main 分支的 push 操作(编译和测试/代码检查)
tags:
- 'v*' # 监听以 'v' 开头的标签的 push 操作(发布 Release)

jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.23.x"
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest

test:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ "1.22.x", "1.23.x" ]
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}

- name: Run test
run: make test COVERAGE_DIR=/tmp/coverage

- name: Send goveralls coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: /tmp/coverage/combined.txt
flag-name: Go-${{ matrix.go }}
parallel: true
if: ${{ github.event.repository.fork == false }} # 仅在非 fork 时上传覆盖率

check-coverage:
name: Check coverage
needs: [ test ]
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
with:
parallel-finished: true
if: ${{ github.event.repository.fork == false }} # 仅在非 fork 时检查覆盖率

# 发布 Release
release:
name: Release a new version
needs: [ lint, test ]
runs-on: ubuntu-latest
# 仅在推送标签时执行 - && - 仅在非 fork 时执行发布
if: ${{ github.event.repository.fork == false && success() && startsWith(github.ref, 'refs/tags/v') }}
steps:
# 1. 检出代码
- name: Checkout code
uses: actions/checkout@v4

# 2. 创建 Release 和上传源码包
- name: Create Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
COVERAGE_DIR ?= .coverage

# cp from: https://github.com/yyle88/gormcngen/blob/5f75d814c71ec306b276a804c134de6655913951/Makefile#L4
test:
@-rm -r $(COVERAGE_DIR)
@mkdir $(COVERAGE_DIR)
make test-with-flags TEST_FLAGS='-v -race -covermode atomic -coverprofile $$(COVERAGE_DIR)/combined.txt -bench=. -benchmem -timeout 20m'

test-with-flags:
@go test $(TEST_FLAGS) ./...
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
# xjson
xjson is a generic JSON parser that depends on `github.com/bitly/go-simplejson`, enhancing type safety and flexibility in JSON processing, while requiring at least Go 1.22 (with generics).
[![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/yyle88/simplejsonx/release.yml?branch=main&label=BUILD)](https://github.com/yyle88/simplejsonx/actions/workflows/release.yml?query=branch%3Amain)
[![GoDoc](https://pkg.go.dev/badge/github.com/yyle88/simplejsonx)](https://pkg.go.dev/github.com/yyle88/simplejsonx)
[![Coverage Status](https://img.shields.io/coveralls/github/yyle88/simplejsonx/master.svg)](https://coveralls.io/github/yyle88/simplejsonx?branch=main)
![Supported Go Versions](https://img.shields.io/badge/Go-1.22%2C%201.23-lightgrey.svg)
[![GitHub Release](https://img.shields.io/github/release/yyle88/simplejsonx.svg)](https://github.com/yyle88/simplejsonx/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/yyle88/simplejsonx)](https://goreportcard.com/report/github.com/yyle88/simplejsonx)

# simplejsonx
simplejsonx is a generic JSON parser that depends on `github.com/bitly/go-simplejson`, enhancing type safety and flexibility in JSON processing, while requiring at least Go 1.22 (with generics).
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/yyle88/simplejsonx

go 1.22.8

require (
github.com/bitly/go-simplejson v0.5.1
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.10.0
github.com/yyle88/tern v0.0.4
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yyle88/tern v0.0.4 h1:PFirncRmdzpMZl6G41Z0Ihmx/XB6PdSQWAHa4a1+PQk=
github.com/yyle88/tern v0.0.4/go.mod h1:g9weyOMLtXYyt37EAshZPUTTHdH7WFauuxDi4oPr7/8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
22 changes: 22 additions & 0 deletions simplejsonx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package simplejsonx

import (
"github.com/bitly/go-simplejson"
"github.com/pkg/errors"
)

// Load creates a simplejson.Json instance from bytes, same as simplejson.NewJson(data).
func Load(data []byte) (simpleJson *simplejson.Json, err error) {
simpleJson, err = simplejson.NewJson(data)
if err != nil {
return nil, errors.WithMessage(err, "unable to parse JSON")
}
return simpleJson, nil
}

// Wrap creates a simplejson.Json instance with the provided value as the root element.
func Wrap(value interface{}) (simpleJson *simplejson.Json) {
simpleJson = simplejson.New()
simpleJson.SetPath([]string{}, value)
return simpleJson
}
76 changes: 76 additions & 0 deletions simplejsonx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package simplejsonx_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/yyle88/simplejsonx"
)

func TestLoad_ValidJSON(t *testing.T) {
data := []byte(`{"key": "value"}`)

simpleJson, err := simplejsonx.Load(data)
require.NoError(t, err)

res, err := simpleJson.Get("key").String()
require.NoError(t, err)
require.Equal(t, "value", res)
}

func TestLoad_InvalidJSON(t *testing.T) {
data := []byte(`invalid message`)

simpleJson, err := simplejsonx.Load(data)
require.Error(t, err)
require.Nil(t, simpleJson)
}

func TestLoad_EmptyInput(t *testing.T) {
data := []byte(``)

simpleJson, err := simplejsonx.Load(data)
require.Error(t, err)
require.Nil(t, simpleJson)
}

func TestWrap(t *testing.T) {
value := map[string]interface{}{
"key": "abc",
}

simpleJson := simplejsonx.Wrap(value)
require.NotNil(t, simpleJson)

res, err := simpleJson.Get("key").String()
require.NoError(t, err)
require.Equal(t, "abc", res)
}

func TestWrap_WithPrimitiveValue(t *testing.T) {
value := 88

simpleJson := simplejsonx.Wrap(value)
require.NotNil(t, simpleJson)

res, err := simpleJson.Int()
require.NoError(t, err)
require.Equal(t, 88, res)
}

func TestWrap_InvalidNone(t *testing.T) {
simpleJson := simplejsonx.Wrap(nil)
require.NotNil(t, simpleJson)

{
res, err := simpleJson.Int()
require.Error(t, err)
require.Equal(t, 0, res)
}

{
res, err := simpleJson.Get("abc").String()
require.Error(t, err)
require.Equal(t, "", res)
}
}
100 changes: 100 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package simplejsonx

import (
"github.com/bitly/go-simplejson"
"github.com/pkg/errors"
"github.com/yyle88/tern"
)

// Extract retrieves the value associated with the given key in the JSON object.
func Extract[T any](simpleJson *simplejson.Json, key string) (T, error) {
if simpleJson == nil {
return tern.Zero[T](), errors.New("parameter simpleJson is missing")
}
return Resolve[T](simpleJson.Get(key))
}

/*
Resolve extracts the value from the provided JSON and convert it to typed value.
Supports the following functions:
func (j *Json) Int() (int, error)
func (j *Json) Int64() (int64, error)
func (j *Json) Float64() (float64, error)
func (j *Json) String() (string, error)
func (j *Json) Uint64() (uint64, error)
func (j *Json) Bool() (bool, error)
func (j *Json) StringArray() ([]string, error)
func (j *Json) Array() ([]interface{}, error)
func (j *Json) Map() (map[string]interface{}, error)
func (j *Json) Bytes() ([]byte, error)
*/

func Resolve[T any](simpleJson *simplejson.Json) (T, error) {
if simpleJson == nil {
return tern.Zero[T](), errors.New("parameter simpleJson is missing")
}
switch zero := tern.Zero[T](); any(zero).(type) {
case int:
res, err := simpleJson.Int()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to int")
}
return any(res).(T), nil
case int64:
res, err := simpleJson.Int64()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to int64")
}
return any(res).(T), nil
case float64:
res, err := simpleJson.Float64()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to float64")
}
return any(res).(T), nil
case string:
res, err := simpleJson.String()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to string")
}
return any(res).(T), nil
case uint64:
res, err := simpleJson.Uint64()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to uint64")
}
return any(res).(T), nil
case bool:
res, err := simpleJson.Bool()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to bool")
}
return any(res).(T), nil
case []string:
res, err := simpleJson.StringArray()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to []string")
}
return any(res).(T), nil
case []interface{}:
res, err := simpleJson.Array()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to []interface{}")
}
return any(res).(T), nil
case map[string]interface{}:
res, err := simpleJson.Map()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to map[string]interface{}")
}
return any(res).(T), nil
case []byte:
res, err := simpleJson.Bytes()
if err != nil {
return zero, errors.WithMessage(err, "unable to resolve JSON value to []byte")
}
return any(res).(T), nil
default:
return zero, errors.Errorf("unsupported generic type: %T. unable to resolve JSON value.", zero)
}
}
Loading

0 comments on commit 3dc0458

Please sign in to comment.