Skip to content

Commit

Permalink
Added createJsonKeys
Browse files Browse the repository at this point in the history
  • Loading branch information
SymeonOclocher committed Feb 19, 2024
1 parent e9527ee commit d296db0
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 96 deletions.
16 changes: 16 additions & 0 deletions json_annotation/lib/src/json_serializable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ class JsonSerializable {
/// such as [fieldRename].
final bool? createFieldMap;

/// If `true` (defaults to false), a private class `_$ExampleJsonKeys`
/// constant is created in the generated part file.
///
/// This class will contain every property, with the json key as value,
/// exposing a secured way to access the json key from the property.
///
/// ```dart
/// @JsonSerializable(createJsonKeys: true)
/// class Example {
/// // ...
/// static const jsonKeys = _$PublicationImplJsonKeys();
/// }
/// ```
final bool? createJsonKeys;

/// If `true` (defaults to false), a private, static `_$ExamplePerFieldToJson`
/// abstract class will be generated in the part file.
///
Expand Down Expand Up @@ -247,6 +262,7 @@ class JsonSerializable {
this.checked,
this.constructor,
this.createFieldMap,
this.createJsonKeys,
this.createFactory,
this.createToJson,
this.disallowUnrecognizedKeys,
Expand Down
8 changes: 6 additions & 2 deletions json_annotation/lib/src/json_serializable.g.dart

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

160 changes: 81 additions & 79 deletions json_serializable/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
<!-- This content is generated. See tool/readme/readme_template.md -->
[![Pub Package](https://img.shields.io/pub/v/json_serializable.svg)](https://pub.dev/packages/json_serializable)

Provides [Dart Build System] builders for handling JSON.
Provides [Dart Build System][Dart Build System] builders for handling JSON.

The builders generate code when they find members annotated with classes defined
in [package:json_annotation].
in [package:json_annotation][package:json_annotation].

- To generate to/from JSON code for a class, annotate it with
[`JsonSerializable`]. You can provide arguments to [`JsonSerializable`] to
configure the generated code. You can also customize individual fields by
annotating them with [`JsonKey`] and providing custom arguments. See the
table below for details on the [annotation values](#annotation-values).

- To generate a Dart field with the contents of a file containing JSON, use the
[`JsonLiteral`] annotation.

## Setup

To configure your project for the latest released version of
`json_serializable`, see the [example].
`json_serializable`, see the [example][example].

## Example

Expand Down Expand Up @@ -85,8 +84,8 @@ code will be generated when you build. There are three ways to control how code
is generated:

1. Setting properties on [`JsonKey`] annotating the target field.
1. Set properties on [`JsonSerializable`] annotating the target type.
1. Add configuration to `build.yaml`[see below](#build-configuration).
2. Set properties on [`JsonSerializable`] annotating the target type.
3. Add configuration to `build.yaml`[see below](#build-configuration).

Every [`JsonSerializable`] field is configurable via `build.yaml`. If you find
you want all or most of your classes with the same configuration, it may be
Expand All @@ -106,7 +105,7 @@ Annotate `enum` types with [`JsonEnum`] (new in `json_annotation` 4.2.0) to:
1. Specify the default rename logic for each enum value using `fieldRename`. For
instance, use `fieldRename: FieldRename.kebab` to encode `enum` value
`noGood` as `"no-good"`.
1. Force the generation of the `enum` helpers, even if the `enum` is not
2. Force the generation of the `enum` helpers, even if the `enum` is not
referenced in code. This is an edge scenario, but useful for some.

Annotate `enum` values with [`JsonValue`] to specify the encoded value to map
Expand Down Expand Up @@ -147,16 +146,20 @@ enum StatusCodeEnhanced {

Out of the box, `json_serializable` supports many common types in the
[dart:core](https://api.dart.dev/stable/dart-core/dart-core-library.html)
library:
library:

[`BigInt`], [`bool`], [`DateTime`], [`double`], [`Duration`], [`Enum`], [`int`],
[`Iterable`], [`List`], [`Map`], [`num`], [`Object`], [`Record`], [`Set`],
[`String`], [`Uri`]

The collection types –

[`Iterable`], [`List`], [`Map`], [`Record`], [`Set`]

– can contain values of all the above types.

For [`Map`], the key value must be one of

[`BigInt`], [`DateTime`], [`Enum`], [`int`], [`Object`], [`String`], [`Uri`]

# Custom types and custom encoding
Expand All @@ -169,92 +172,90 @@ customize the encoding/decoding of any type, you have a few options.
for these types, you don't have to! The generator code only looks for these
methods. It doesn't care how they were created.

```dart
@JsonSerializable()
class Sample1 {
Sample1(this.value);
factory Sample1.fromJson(Map<String, dynamic> json) =>
_$Sample1FromJson(json);
```dart
@JsonSerializable()
class Sample1 {
Sample1(this.value);
// Sample2 is NOT annotated with @JsonSerializable(), but that's okay
// The class has a `fromJson` constructor and a `toJson` method, which is
// all that is required.
final Sample2 value;
factory Sample1.fromJson(Map<String, dynamic> json) =>
_$Sample1FromJson(json);
Map<String, dynamic> toJson() => _$Sample1ToJson(this);
}
// Sample2 is NOT annotated with @JsonSerializable(), but that's okay
// The class has a `fromJson` constructor and a `toJson` method, which is
// all that is required.
final Sample2 value;
class Sample2 {
Sample2(this.value);
Map<String, dynamic> toJson() => _$Sample1ToJson(this);
}
// The convention is for `fromJson` to take a single parameter of type
// `Map<String, dynamic>` but any JSON-compatible type is allowed.
factory Sample2.fromJson(int value) => Sample2(value);
final int value;
class Sample2 {
Sample2(this.value);
// The convention is for `toJson` to take return a type of
// `Map<String, dynamic>` but any JSON-compatible type is allowed.
int toJson() => value;
}
```
// The convention is for `fromJson` to take a single parameter of type
// `Map<String, dynamic>` but any JSON-compatible type is allowed.
factory Sample2.fromJson(int value) => Sample2(value);
final int value;
1. Use the [`JsonKey.toJson`] and [`JsonKey.fromJson`] properties to specify
// The convention is for `toJson` to take return a type of
// `Map<String, dynamic>` but any JSON-compatible type is allowed.
int toJson() => value;
}
```
2. Use the [`JsonKey.toJson`] and [`JsonKey.fromJson`] properties to specify
custom conversions on the annotated field. The functions specified must be
top-level or static. See the documentation of these properties for details.

```dart
@JsonSerializable()
class Sample3 {
Sample3(this.value);
factory Sample3.fromJson(Map<String, dynamic> json) =>
_$Sample3FromJson(json);
```dart
@JsonSerializable()
class Sample3 {
Sample3(this.value);
@JsonKey(
toJson: _toJson,
fromJson: _fromJson,
)
final DateTime value;
factory Sample3.fromJson(Map<String, dynamic> json) =>
_$Sample3FromJson(json);
Map<String, dynamic> toJson() => _$Sample3ToJson(this);
@JsonKey(
toJson: _toJson,
fromJson: _fromJson,
)
final DateTime value;
static int _toJson(DateTime value) => value.millisecondsSinceEpoch;
static DateTime _fromJson(int value) =>
DateTime.fromMillisecondsSinceEpoch(value);
}
```
Map<String, dynamic> toJson() => _$Sample3ToJson(this);
1. Create an implementation of [`JsonConverter`] and annotate either the
static int _toJson(DateTime value) => value.millisecondsSinceEpoch;
static DateTime _fromJson(int value) =>
DateTime.fromMillisecondsSinceEpoch(value);
}
```
3. Create an implementation of [`JsonConverter`] and annotate either the
corresponding field or the containing class. [`JsonConverter`] is convenient
if you want to use the same conversion logic on many fields. It also allows
you to support a type within collections. Check out
[these examples](https://github.com/google/json_serializable.dart/blob/master/example/lib/json_converter_example.dart).

```dart
@JsonSerializable()
class Sample4 {
Sample4(this.value);
```dart
@JsonSerializable()
class Sample4 {
Sample4(this.value);
factory Sample4.fromJson(Map<String, dynamic> json) =>
_$Sample4FromJson(json);
factory Sample4.fromJson(Map<String, dynamic> json) =>
_$Sample4FromJson(json);
@EpochDateTimeConverter()
final DateTime value;
@EpochDateTimeConverter()
final DateTime value;
Map<String, dynamic> toJson() => _$Sample4ToJson(this);
}
Map<String, dynamic> toJson() => _$Sample4ToJson(this);
}
class EpochDateTimeConverter implements JsonConverter<DateTime, int> {
const EpochDateTimeConverter();
class EpochDateTimeConverter implements JsonConverter<DateTime, int> {
const EpochDateTimeConverter();
@override
DateTime fromJson(int json) => DateTime.fromMillisecondsSinceEpoch(json);
@override
DateTime fromJson(int json) => DateTime.fromMillisecondsSinceEpoch(json);
@override
int toJson(DateTime object) => object.millisecondsSinceEpoch;
}
```
@override
int toJson(DateTime object) => object.millisecondsSinceEpoch;
}
```

# Build configuration

Expand All @@ -276,6 +277,7 @@ targets:
constructor: ""
create_factory: true
create_field_map: false
create_json_keys: false
create_per_field_to_json: false
create_to_json: true
disallow_unrecognized_keys: false
Expand All @@ -297,15 +299,15 @@ targets:
[`Enum`]: https://api.dart.dev/stable/dart-core/Enum-class.html
[`int`]: https://api.dart.dev/stable/dart-core/int-class.html
[`Iterable`]: https://api.dart.dev/stable/dart-core/Iterable-class.html
[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonConverter-class.html
[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonEnum/valueField.html
[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonEnum-class.html
[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonKey/fromJson.html
[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonKey/toJson.html
[`JsonKey`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonKey-class.html
[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonLiteral-class.html
[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonSerializable-class.html
[`JsonValue`]: https://pub.dev/documentation/json_annotation/4.8.1/json_annotation/JsonValue-class.html
[`JsonConverter`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum/valueField.html
[`JsonEnum`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonEnum-class.html
[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html
[`JsonKey`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey-class.html
[`JsonLiteral`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonLiteral-class.html
[`JsonSerializable`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable-class.html
[`JsonValue`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonValue-class.html
[`List`]: https://api.dart.dev/stable/dart-core/List-class.html
[`Map`]: https://api.dart.dev/stable/dart-core/Map-class.html
[`num`]: https://api.dart.dev/stable/dart-core/num-class.html
Expand Down
20 changes: 20 additions & 0 deletions json_serializable/lib/src/encoder_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ mixin EncodeHelper implements HelperCore {
return buffer.toString();
}

/// Generates an object containing metadatas related to the encoding,
/// destined to be used by other code-generators.
String createJsonKeys(Set<FieldElement> accessibleFieldSet) {
assert(config.createJsonKeys);

final buffer = StringBuffer(
'class _\$${element.name.nonPrivate}JsonKeys {',
)..write('const _\$${element.name.nonPrivate}JsonKeys();');

for (final field in accessibleFieldSet) {
buffer.writeln(
'final String ${field.name} = ${escapeDartString(nameAccess(field))};',
);
}

buffer.write('}');

return buffer.toString();
}

Iterable<String> createToJson(Set<FieldElement> accessibleFields) sync* {
assert(config.createToJson);

Expand Down
4 changes: 4 additions & 0 deletions json_serializable/lib/src/generator_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
yield createFieldMap(accessibleFieldSet);
}

if (config.createJsonKeys) {
yield createJsonKeys(accessibleFieldSet);
}

if (config.createPerFieldToJson) {
yield createPerFieldToJson(accessibleFieldSet);
}
Expand Down
6 changes: 6 additions & 0 deletions json_serializable/lib/src/type_helpers/config_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class ClassConfig {
final bool createFactory;
final bool createToJson;
final bool createFieldMap;
final bool createJsonKeys;
final bool createPerFieldToJson;
final bool disallowUnrecognizedKeys;
final bool explicitToJson;
Expand All @@ -66,6 +67,7 @@ class ClassConfig {
required this.createFactory,
required this.createToJson,
required this.createFieldMap,
required this.createJsonKeys,
required this.createPerFieldToJson,
required this.disallowUnrecognizedKeys,
required this.explicitToJson,
Expand All @@ -85,6 +87,8 @@ class ClassConfig {
constructor: config.constructor ?? ClassConfig.defaults.constructor,
createFieldMap:
config.createFieldMap ?? ClassConfig.defaults.createFieldMap,
createJsonKeys:
config.createJsonKeys ?? ClassConfig.defaults.createJsonKeys,
createPerFieldToJson: config.createPerFieldToJson ??
ClassConfig.defaults.createPerFieldToJson,
createFactory:
Expand Down Expand Up @@ -113,6 +117,7 @@ class ClassConfig {
createFactory: true,
createToJson: true,
createFieldMap: false,
createJsonKeys: false,
createPerFieldToJson: false,
disallowUnrecognizedKeys: false,
explicitToJson: false,
Expand All @@ -129,6 +134,7 @@ class ClassConfig {
createFactory: createFactory,
createToJson: createToJson,
createFieldMap: createFieldMap,
createJsonKeys: createJsonKeys,
createPerFieldToJson: createPerFieldToJson,
ignoreUnannotated: ignoreUnannotated,
explicitToJson: explicitToJson,
Expand Down
Loading

0 comments on commit d296db0

Please sign in to comment.