Skip to content

Commit

Permalink
feat: add go omitempty json tag
Browse files Browse the repository at this point in the history
  • Loading branch information
Souvikns committed May 20, 2024
1 parent a994e02 commit f90a1b6
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 9 deletions.
8 changes: 8 additions & 0 deletions docs/languages/Go.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@ As you normally only need one library to do this, we developers can never get en
Here are all the supported presets and the libraries they use for converting to and from JSON:

- [JSON Tags](#json-tags)
- [omitempty tag](#omitempty-tag)

#### JSON Tags

To generate go models that work correctly with JSON marshal functions we need to generate appropriate JSON `struct-tags`, use the preset `GO_COMMON_PRESET` and provide the option `addJsonTag: true`.

check out this [example for a live demonstration](../../examples/go-json-tags/)

##### omitempty tag

To add `omitempty` parameter to JSON `struct-tags`, use the preset `GO_COMMON_PRESET` and provide the option `addOmitEmpty: true` along with `addJsonTag: true`.

check out this [example for a live demonstration](../../examples/go-omitepty-tag/)


### To and from XML
Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)!

Expand Down
17 changes: 17 additions & 0 deletions examples/go-omitepty-tag/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# go omitempty JSON struct tag

`GO_COMMON_PRESET` to render `omitempty` parameter in JSON `struct-tags` in go struct.

## How to run this example

Run this example using:

```sh
npm i && npm run start
```

If you are on Windows, use the `start:windows` script instead:

```sh
npm i && npm run start:windows
```
97 changes: 97 additions & 0 deletions examples/go-omitepty-tag/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Should be able to render TEMPLATE and should log expected output to console 1`] = `
Array [
"// Root represents a Root model.
type Root struct {
Cities *Cities \`json:\\"cities, omitempty\\"\`
Options *Options \`json:\\"options, omitempty\\"\`
}",
]
`;
exports[`Should be able to render TEMPLATE and should log expected output to console 2`] = `
Array [
"// Cities represents an enum of Cities.
type Cities uint
const (
CitiesLondon Cities = iota
CitiesRome
CitiesBrussels
)
// Value returns the value of the enum.
func (op Cities) Value() any {
if op >= Cities(len(CitiesValues)) {
return nil
}
return CitiesValues[op]
}
var CitiesValues = []any{\\"London\\",\\"Rome\\",\\"Brussels\\"}
var ValuesToCities = map[any]Cities{
CitiesValues[CitiesLondon]: CitiesLondon,
CitiesValues[CitiesRome]: CitiesRome,
CitiesValues[CitiesBrussels]: CitiesBrussels,
}
func (op *Cities) UnmarshalJSON(raw []byte) error {
var v any
if err := json.Unmarshal(raw, &v); err != nil {
return err
}
*op = ValuesToCities[v]
return nil
}
func (op Cities) MarshalJSON() ([]byte, error) {
return json.Marshal(op.Value())
}",
]
`;
exports[`Should be able to render TEMPLATE and should log expected output to console 3`] = `
Array [
"// Options represents an enum of Options.
type Options uint
const (
OptionsNumber_123 Options = iota
OptionsNumber_213
OptionsTrue
OptionsRun
)
// Value returns the value of the enum.
func (op Options) Value() any {
if op >= Options(len(OptionsValues)) {
return nil
}
return OptionsValues[op]
}
var OptionsValues = []any{123,213,true,\\"Run\\"}
var ValuesToOptions = map[any]Options{
OptionsValues[OptionsNumber_123]: OptionsNumber_123,
OptionsValues[OptionsNumber_213]: OptionsNumber_213,
OptionsValues[OptionsTrue]: OptionsTrue,
OptionsValues[OptionsRun]: OptionsRun,
}
func (op *Options) UnmarshalJSON(raw []byte) error {
var v any
if err := json.Unmarshal(raw, &v); err != nil {
return err
}
*op = ValuesToOptions[v]
return nil
}
func (op Options) MarshalJSON() ([]byte, error) {
return json.Marshal(op.Value())
}",
]
`;
17 changes: 17 additions & 0 deletions examples/go-omitepty-tag/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const spy = jest.spyOn(global.console, 'log').mockImplementation(() => {
return;
});
import { generate } from './index';

describe('Should be able to render TEMPLATE', () => {
afterAll(() => {
jest.restoreAllMocks();
});
test('and should log expected output to console', async () => {
await generate();
expect(spy.mock.calls.length).toEqual(3);
expect(spy.mock.calls[0]).toMatchSnapshot();
expect(spy.mock.calls[1]).toMatchSnapshot();
expect(spy.mock.calls[2]).toMatchSnapshot();
});
});
37 changes: 37 additions & 0 deletions examples/go-omitepty-tag/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
GoGenerator,
GO_COMMON_PRESET,
GoCommonPresetOptions
} from '../../src';

const options: GoCommonPresetOptions = { addJsonTag: true, addOmitEmpty: true };
const generator = new GoGenerator({
presets: [{ preset: GO_COMMON_PRESET, options }]
});
const jsonSchemaDraft7 = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
additionalProperties: false,
properties: {
cities: {
$id: 'cities',
type: 'string',
enum: ['London', 'Rome', 'Brussels']
},
options: {
$id: 'options',
type: ['integer', 'boolean', 'string'],
enum: [123, 213, true, 'Run']
}
}
};

export async function generate(): Promise<void> {
const models = await generator.generate(jsonSchemaDraft7);
for (const model of models) {
console.log(model.result);
}
}
if (require.main === module) {
generate();
}
10 changes: 10 additions & 0 deletions examples/go-omitepty-tag/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions examples/go-omitepty-tag/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"config" : { "example_name" : "go omitempty json tag" },
"scripts": {
"install": "cd ../.. && npm i",
"start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts",
"start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts",
"test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts",
"test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts"
}
}
5 changes: 3 additions & 2 deletions modelina-cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions src/generators/go/presets/CommonPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,25 @@ import {

export interface GoCommonPresetOptions {
addJsonTag: boolean;
addOmitEmpty?: boolean;
}

function renderJSONTag({
field
field,
omitempty
}: {
field: ConstrainedObjectPropertyModel;
omitempty?: boolean;
}): string {
if (
field.property instanceof ConstrainedDictionaryModel &&
field.property.serializationType === 'unwrap'
) {
return `json:"-"`;
}
return `json:"${field.unconstrainedPropertyName}"`;
return `json:"${field.unconstrainedPropertyName}${
omitempty ? ', omitempty' : ''
}"`;
}

function renderMarshallingFunctions({
Expand Down Expand Up @@ -52,7 +57,7 @@ export const GO_COMMON_PRESET: GoPreset<GoCommonPresetOptions> = {
field: ({ content, field, options }) => {
const blocks: string[] = [];
if (options.addJsonTag) {
blocks.push(renderJSONTag({ field }));
blocks.push(renderJSONTag({ field, omitempty: options.addOmitEmpty }));
}
return `${content} \`${blocks.join(' ')}\``;
}
Expand Down
5 changes: 4 additions & 1 deletion test/generators/go/presets/CommonPreset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
describe('GO_COMMON_PRESET', () => {
let generator: GoGenerator;
beforeEach(() => {
const options: GoCommonPresetOptions = { addJsonTag: true };
const options: GoCommonPresetOptions = {
addJsonTag: true,
addOmitEmpty: true
};
generator = new GoGenerator({
presets: [{ preset: GO_COMMON_PRESET, options }]
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
exports[`GO_COMMON_PRESET should render json tags for structs 1`] = `
"// Root represents a Root model.
type Root struct {
StringProp string \`json:\\"stringProp\\"\`
NumberProp float64 \`json:\\"numberProp\\"\`
BooleanProp bool \`json:\\"booleanProp\\"\`
StringProp string \`json:\\"stringProp, omitempty\\"\`
NumberProp float64 \`json:\\"numberProp, omitempty\\"\`
BooleanProp bool \`json:\\"booleanProp, omitempty\\"\`
}"
`;
Expand Down

0 comments on commit f90a1b6

Please sign in to comment.