Using Flutter Freezed to generate code to parse a Json Object

6.7k Views Asked by At

I'm trying to parse some JSON from a file and decided to use Freezed to generate the code. The problem is that (as far as i can tell) there's no way to use a JSON object's name.

So say I have the following JSON object:

{
  "uniqueName":{
    "website": "https://www.example.com/",
    "description": "Some description",
    "hosted_demo": false,
    "demo": "",
    "oss": false,
    "source": "",
    "other-links": [
      {
        "title": "Clients",
        "site": "https://shlink.io/apps"
      }
    ],
    "license": "MIT"
  }
}

Then this would be the required dart code for Freezed code (done with instructions from this site):

// 1. import freezed_annotation
import 'package:freezed_annotation/freezed_annotation.dart';

// import any other models we depend on
import 'otherLinks.dart';

// 2. add 'part' files
part 'freezed_files/item.freezed.dart';
part 'generated/item.g.dart';

// 3. add @freezed annotation
@freezed
// 4. define a class with a mixin
class Item with _$Item {
  // 5. define a factory constructor
  factory Item(
      {
      // 6. list all the arguments/properties
      @Default("") String website,
      @Default("") String description,
      // ignore: invalid_annotation_target
      @Default(false) @JsonKey(name: 'hosted_demo') bool? hostedDemo,
      @Default("") String demo,
      @Default(false) bool oss,
      @Default("") String source,
      // ignore: invalid_annotation_target
      @Default([]) @JsonKey(name: 'other-links') List<OtherLinks> otherLinks,
      @Default("") String license
      // 7. assign it with the `_Item` class constructor
      }) = _Item;

  // 8. define another factory constructor to parse from json
  factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
}

But i have no idea how to get the uniqueName into the data class. Most other places I've checked assume that the JSON data is formatted with the uniqueName inside JSON object with its own key. While refactoring the JSON file is an option, I would rather not. The whole JSON file is about 12000 lines, making refactoring it a pain.

Do you folks have any idea how I can get uniqueName into the data class?

2

There are 2 best solutions below

1
On
import 'package:freezed_annotation/freezed_annotation.dart';

part 'item.freezed.dart';
part 'item.g.dart';

@freezed
class Item with _$Item {
  factory Item({
    @Default(UniqueName) UniqueName uniqueName,
  }) = _Item;

  factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
}

@freezed
class UniqueName with _$UniqueName {
  factory UniqueName({
    @Default('') String website,
    @Default('') String description,
    @Default(false) bool hostedDemo,
    @Default('') String demo,
    @Default(false) bool oss,
    @Default('') String source,
    @Default([]) @JsonKey(name: 'other-links') List<OtherLink> otherLinks,
    @Default('') String license,
  }) = _UniqueName;

  factory UniqueName.fromJson(Map<String, dynamic> json) =>
      _$UniqueNameFromJson(json);
}

@freezed
class OtherLink with _$OtherLink {
  factory OtherLink({
    @Default('') String title,
    @Default('') String site,
  }) = _OtherLink;

  factory OtherLink.fromJson(Map<String, dynamic> json) =>
      _$OtherLinkFromJson(json);
}
0
On

I'm not sure if this what you're looking for, but how about this?

import 'package:freezed_annotation/freezed_annotation.dart';

import 'otherLinks.dart';

part 'freezed_files/item.freezed.dart';
part 'generated/item.g.dart';

@freezed
class Item with _$Item {
  factory Item({
    // Add a new property to save the uniqueName
    @Default("") String uniqueName,
    @Default("") String website,
    @Default("") String description,
    @Default(false) @JsonKey(name: 'hosted_demo') bool? hostedDemo,
    @Default("") String demo,
    @Default(false) bool oss,
    @Default("") String source,
    @Default([]) @JsonKey(name: 'other-links') List<OtherLinks> otherLinks,
    @Default("") String license
  }) = _Item;

  factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);

  // Add this new function
  factory Item.fromUniqueJson(Map<String, dynamic> json) {
    // Get the uniqueName string key and save it
    var uniqueNameKey = json.keys.first;

    // Convert the rest of the json object beneath the uniqueName key
    return Item.fromJson(json[uniqueNameKey])
      // Return the new Item with the uniqueName saved as a property
      .copyWith(uniqueName: uniqueNameKey);
  }
}