Heavyweight helpers for building schema.org JSON‑LD objects in Dart.
This package provides typed classes and union-pattern helpers to build JSON-LD payloads for common schema.org entities (Person, Organization, Article, etc.) and serialize them for use in websites or metadata pipelines.
Repository: https://github.com/antinna/json_ld
YouTube reference and walkthroughs: https://youtube.com/antinna
If you find this useful, please consider sponsoring Manish: https://github.com/sponsors/Manishmg3994
Add the dependency to your pubspec.yaml:
dependencies:
json_ld: ^1.0.0-alpha.1dart pub add json_ldThen run:
dart pub getSee the example/main.dart for a runnable example: example/main.dart
Example — creating simple Person and Organization entities and printing JSON-LD:
// Example 1: Create a Person entity
final person = Person()
..name = ['Jane Doe']
..givenName = ['Jane']
..familyName = ['Doe']
..email = ['jane.doe@example.com'];
print('Person JSON-LD:');
print(person);
print('');
// Example 2: Create an Organization entity
final organization = Organization()
..name = ['Example Corp']
..url = ['https://example.com']
..email = ['contact@example.com']
..sameAs = ['https://twitter.com/examplecorp'];
print('Organization JSON-LD:');
print(organization);
print('');You can also build richer objects, e.g. Article with author, publisher, and image — see the example for more patterns.
When serializing JSON-LD, fields stored as List<T> are collapsed to a single scalar value when the list contains exactly one primitive or text item. If the list contains multiple items, it is serialized as a JSON array.
Example:
- Single-item values are emitted as scalar fields:
"name": "Jane Doe"- Multiple values are emitted as arrays:
"name": ["Jane Doe", "Manish Sharma"]So a Person with a single name, single email, and single familyName can serialize as a compact object.
- Example code: example/main.dart
- Patterns for union types are demonstrated in the
lib/srcsources. - Video walkthroughs: https://youtube.com/antinna
Follow this pattern for each union type in the library to keep parsing and JSON (de)serialization consistent and explicit.
// Pattern for Each Union Type:
sealed class NameOrName { ... factory fromJson() ... }
class NameOrNameType1 extends NameOrName { ... }
class NameOrNameType2 extends NameOrName { ... }
class NameOrNameString extends NameOrName { ... }Use a single sealed base type with concrete subclasses for each variant (including a typed string wrapper variant). Provide a fromJson factory on the sealed base to dispatch the correct subtype at runtime.
For union types such as LanguageOrText, the wrapper classes can be either LanguageOrTextLanguage or LanguageOrTextString.
final languageValue = LanguageOrTextString('en');
// or for a Language object:
final languageObject = LanguageOrTextLanguage(Language()..name = ['English']);This means the union utilization for LanguageOrText can be understood as:
LanguageOrText=> wrapperLanguageOrTextLanguageorLanguageOrTextString
Concrete union wrapper classes are named like LanguageOrTextLanguage and LanguageOrTextString in generated code.
The generator uses a reservedNames set to avoid generating Dart identifiers that would conflict with Dart core types or generated class methods/fields.
Reserved names include:
- Dart types:
Map,List,Set,Object,String,bool,num,int,double,DateTime,Duration,Type,Enum,Null - Generated members:
map,type,id,context,toJson,fromJson,toString,value
When a schema name matches one of these reserved values, cleanName prefixes it with Schema.
Examples:
schema:ObjectbecomesSchemaObjectschema:ListbecomesSchemaList- a property name like
idbecomesschemaid
This prevents invalid or dangerous Dart names and avoids collisions with generated base class fields/methods.
For generated classes:
schema:Objectdoes not produceclass Objectschema:Stringdoes not produceclass Stringschema:DateTimedoes not produceclass DateTime
Instead it produces:
class SchemaObjectclass SchemaStringclass SchemaDateTime
For generated fields:
idbecomesschemaidcontextbecomesschemacontexttoJsonbecomesschematoJson
This avoids collisions with the base model's id, context, toJson(), and fromJson().
Union type names are built from cleaned schema names using getUnionName(ranges) and joined with Or.
If a union component is reserved, the generated union name remains safe.
Examples:
schema:Objectin a union becomesSchemaObjectOr...- primitive wrappers are normalized to typed variants like
${name}String,${name}Num, or${name}Bool
In short, the reservedNames set protects generated Dart code from invalid identifiers, core type collisions, method/property collisions, and unsafe union naming.
- This library currently covers over 800 classes and 1500 properties across schema.org types. Coverage may expand over time.
- This package includes a large number of types and helpers. Because of its size, it may increase binary size and could affect runtime performance in constrained environments. Use selective imports or tree-shaking when embedding in performance-sensitive apps.
- The maintainers provide tools for constructing JSON-LD but cannot guarantee that generated payloads are free from harmful or unintended content. Validate and sanitize inputs before publishing metadata or using it in production.
Warning
This package is primarily intended for developers who want to explore, learn, or experiment with schema.org and JSON-LD modeling in Dart.
It contains hundreds of generated types and thousands of properties, making it extremely heavyweight. Runtime performance, memory usage, analysis time, code completion, and build times may be significantly affected.
For best results, a supercomputer is recommended. (Only partially joking.)
Note
This package is not recommended for production use unless you fully understand and accept the performance trade-offs.
Contributions, bug reports and feature requests are welcome. Please file issues at https://github.com/antinna/json_ld/issues and open PRs against the main branch.
If you'd like to support development, sponsor Manish here: https://github.com/sponsors/Manishmg3994
This package is licensed under the MIT License (2026). See LICENSE for the full text.
Copyright (c) 2026 Antinna