JSON Data Importer

Configurable import pipeline for Unity


Project maintained by MikhailAkushko Hosted on GitHub Pages — Theme by mattgraham

Getting Started

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.

1. Describe the Data

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.

2. Create the Target Asset Class

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:

After compilation, create the asset through the menu specified in CreateAssetMenu.

3. Create ImportSourceConfig

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:

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:

4. What happens automatically

The auto path works when the target asset has a supported shape.

The system can automatically:

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.

5. When a Custom Parser Is Needed

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:

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.

6. When a Custom Target Adapter/Profile Is Needed

A custom adapter is needed when the target asset cannot be described by one of the auto shapes:

In this case, the usual pieces are:

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.

7. Choosing Auto or Custom

Use the auto path when:

A custom parser is needed when:

A custom adapter/profile is needed when:

A custom source provider is needed when:

Execution path details are described in Import Execution Flow. Custom source providers are covered in Custom IImportSourceProvider.