Configurable import pipeline for Unity
This document describes the basic path for wiring a new import target using AutoDocumentSettingsDatabase as an example. It also explains where the system works automatically and when custom parsers/profiles/adapters are used.
First, you need a serializable entry type. This is the type of one imported record or one document.
using System;
namespace Game.Import
{
[Serializable]
public sealed class AutoDocumentSettings
{
public string title;
public int maxLevel;
public bool isEnabled;
public float gravity;
public AutoDocumentLimits limits;
}
[Serializable]
public sealed class AutoDocumentLimits
{
public int minAmount;
public int maxAmount;
}
}
For the auto JSON path, public field names match JSON field names. For example:
{
"title": "Auto Document Settings",
"maxLevel": 12,
"isEnabled": true,
"gravity": 9.81,
"limits": {
"minAmount": 1,
"maxAmount": 99
}
}
If JSON does not match the entry type shape, see When a Custom Parser Is Needed.
This is the target asset: a ScriptableObject that the importer applies data to.
For a single-document scenario, use [ImportTargetMetadata] on the class and [ImportTargetDocument] on the field or property where the document is stored:
using JsonDataImporter.Runtime.Core.Metadata;
using UnityEngine;
namespace Game.Import
{
[ImportTargetMetadata(
"auto_document_settings_database",
"Auto Document Settings Database",
typeof(AutoDocumentSettings))]
[CreateAssetMenu(
fileName = "AutoDocumentSettingsDatabase",
menuName = "Game/Import/Auto Document Settings Database")]
public sealed class AutoDocumentSettingsDatabase : ScriptableObject
{
[ImportTargetDocument]
public AutoDocumentSettings settings;
}
}
Important details:
targetTypeId ("auto_document_settings_database") is stable: it is stored in the config and used for auto profile/parser ids.displayName ("Auto Document Settings Database") is shown in the editor UI.entryType (typeof(AutoDocumentSettings)) tells the pipeline which type to parse, validate, diff, and apply.[ImportTargetDocument] tells the auto adapter that the target stores one document, not a list of records.After compilation, create the asset through the menu specified in CreateAssetMenu.
Create the config asset:
Create > Json Data Importer > Import Source Config
The minimal happy path looks like this:
targetAsset: AutoDocumentSettingsDatabase.asset
source provider: Text Asset
Text Asset: auto_document_settings.json
import profile: Auto Document Settings Database [Auto Document]
Parser: Auto Document Settings Database [Auto JSON Document]
apply strategy: Replace All [One Class]
To get this state, fill only the basic fields:
targetAsset - the created AutoDocumentSettingsDatabase.asset.Text Asset when JSON is stored in a Unity TextAsset.Text Asset inside the source provider - the JSON file with data.Then press Auto for pipeline selection. For a document target, the editor usually selects the auto document profile, auto JSON parser, compatible validators, diff/fingerprint builder, and Replace All (One Class).
By default, backup capture is enabled even when no Execution Profile is assigned. Before the first Apply, either assign a Backup History asset or assign an Execution Profile with Capture Backup On Apply disabled. To create a backup history asset, use:
Create > Json Data Importer > Import Backup History
The full field list and policy override order are described in ImportSourceConfig Reference.
After that, you can press:
Preview - read source, parse entries, build validation/diff.Validate - run the same preview path and return validation status.Apply - run the preview path again, capture a backup when backup capture is enabled, then apply prepared source to the target asset. If backup capture is enabled and no Backup History asset is assigned, Apply stops before changing the target.The auto path works when the target asset has a supported shape.
The system can automatically:
[ImportTargetMetadata];[ImportTargetCollection];[ImportTargetDocument];auto_json::<targetTypeId>;Auto button;ImportJobRunner.Supported auto target shapes:
// Collection: JSON root array or object with an array field named items.
[ImportTargetCollection]
public List<ItemRecord> items = new();
// Document: JSON root object.
[ImportTargetDocument]
public AutoDocumentSettings settings;
// Direct fields: no [ImportTargetDocument]/[ImportTargetCollection],
// when target public fields/properties match entry type by name/type.
public string title;
public int maxLevel;
The auto JSON document parser expects a JSON root object and parses it into entryType through JsonUtility. The auto collection parser expects a root array or an object with an array field whose name matches the [ImportTargetCollection] member name.
A custom parser is needed when source data cannot be parsed directly by the auto JSON or CSV parser into your entry type.
Common reasons:
null needs to be preserved.The following custom examples use this minimal target shape:
using System;
using JsonDataImporter.Runtime.Core.Metadata;
using UnityEngine;
[Serializable]
public sealed class SpaceStation
{
[ImportIdentity]
public string station_id;
public string name;
}
[CreateAssetMenu(
fileName = "HardJsonStationDatabase",
menuName = "Game/Import/Hard JSON Station Database")]
public sealed class HardJsonStationDatabase : ScriptableObject
{
public SpaceStation station;
}
Custom parser shape:
using System;
using System.Collections.Generic;
using JsonDataImporter.Runtime.Core.Metadata;
using JsonDataImporter.Runtime.Core.Source;
[ImportParserMetadata(
"hard_json_station_parser",
"Hard JSON Station Parser [Custom]",
typeof(SpaceStation),
"Custom")]
public sealed class HardJsonStationParser : IImportParser<SpaceStation>
{
public Type EntryType => typeof(SpaceStation);
public IReadOnlyList<SpaceStation> ParseTypedEntries(string rawContent)
{
// Parse any source shape here and return normalized entries.
return new[] { ParseStation(rawContent) };
}
public IReadOnlyList<object> ParseEntries(string rawContent)
{
IReadOnlyList<SpaceStation> typed = ParseTypedEntries(rawContent);
var result = new List<object>(typed.Count);
for (int i = 0; i < typed.Count; i++)
result.Add(typed[i]);
return result;
}
private static SpaceStation ParseStation(string rawContent)
{
// Put custom JSON/file conversion here.
throw new NotImplementedException();
}
}
After compilation, the parser appears in the Parser dropdown when its entryType matches the entry type of the selected target/profile. In the config, select this parser instead of the auto JSON parser. For the full parser template and requirements, see Custom IImportParser.
A custom adapter is needed when the target asset cannot be described by one of the auto shapes:
[ImportTargetCollection] or [ImportTargetDocument];In this case, the usual pieces are:
IImportTargetAdapter - how to read current entries from the target and how to write final entries back;IImportProfile - connects TargetTypeId, DisplayName, EntryType, adapter, backup codec, and default apply strategy;Simplified adapter/profile shape:
using System;
using System.Collections.Generic;
using JsonDataImporter.Runtime.Core;
using JsonDataImporter.Runtime.Core.Apply;
using JsonDataImporter.Runtime.Core.Metadata;
using Object = UnityEngine.Object;
public sealed class HardJsonStationTargetAdapter : IImportTargetAdapter
{
public bool CanHandleTarget(Object targetAsset)
{
return targetAsset is HardJsonStationDatabase;
}
public string GetTargetDisplayName(Object targetAsset)
{
return targetAsset != null ? targetAsset.name : "<null target>";
}
public int GetTargetEntryCount(Object targetAsset)
{
return targetAsset is HardJsonStationDatabase db && db.station != null ? 1 : 0;
}
public IReadOnlyList<object> GetTargetEntries(Object targetAsset)
{
if (targetAsset is not HardJsonStationDatabase db || db.station == null)
return Array.Empty<object>();
return new object[] { db.station };
}
public object CreateTargetEntryFromSource(object sourceEntry)
{
return CloneStation(sourceEntry);
}
public void SetTargetEntries(Object targetAsset, IReadOnlyList<object> finalEntries)
{
if (targetAsset is not HardJsonStationDatabase db)
throw new InvalidOperationException("Invalid target.");
db.station = finalEntries != null && finalEntries.Count > 0
? CloneStation(finalEntries[0])
: null;
}
private static SpaceStation CloneStation(object sourceEntry)
{
if (sourceEntry is not SpaceStation station)
throw new InvalidOperationException("Expected SpaceStation entry.");
// Copy nested objects and collections here as new instances too.
return new SpaceStation
{
station_id = station.station_id,
name = station.name
};
}
}
[ImportProfileMetadata("Hard JSON Station Profile [Custom]", "Custom")]
public sealed class HardJsonStationImportProfile : IImportProfile
{
private readonly HardJsonStationTargetAdapter _targetAdapter = new();
public string TargetTypeId => "hard_json_station_profile";
public string DisplayName => "Hard JSON Station Profile [Custom]";
public Type EntryType => typeof(SpaceStation);
public IImportTargetAdapter TargetAdapter => _targetAdapter;
public IImportBackupCodec BackupCodec { get; } = new JsonEntryListBackupCodec();
public IImportApplyStrategy GetDefaultApplyStrategy()
{
return new OneClassReplaceAllApplyStrategy();
}
}
After compilation, the profile is found by the discovery system if it is a concrete IImportProfile with a public parameterless constructor. In ImportSourceConfig, select this profile in the import profile field. For complete adapter/profile templates, see Custom IImportTargetAdapter and Custom IImportProfile.
Use the auto path when:
[ImportTargetCollection], [ImportTargetDocument], or direct fields;A custom parser is needed when:
A custom adapter/profile is needed when:
A custom source provider is needed when:
Text Asset, File Path, or HTTP providers do not cover the source;Execution path details are described in Import Execution Flow. Custom source providers are covered in Custom IImportSourceProvider.