Quartz.NETQuartz.NET
Home
Features
Discussions
NuGet
GitHub
Home
Features
Discussions
NuGet
GitHub
  • Getting Started

    • Quartz 3 Quick Start
    • Tutorial
      • Using Quartz
      • Library Overview
      • Jobs And Triggers
      • More About Jobs
      • More About Triggers
      • Execution Groups
      • Node Affinity (Preferred Node)
      • Simple Triggers
      • Cron Triggers
      • RecurrenceTrigger
      • Trigger and Job Listeners
      • Scheduler Listeners
      • Job Stores
      • Tuning the Scheduler
      • Configuration, Resource Usage and SchedulerFactory
      • Advanced (Enterprise) Features
    • Configuration Reference
    • JSON Configuration
    • Frequently Asked Questions
    • Best Practices
    • Troubleshooting
    • API Documentation
    • Database Schema
    • Migration Guide
    • Miscellaneous Features
  • How To's

    • One-Off Job
    • Multiple Triggers
    • Job Template
    • Using the CronTrigger
    • Rescheduling Jobs
  • Packages

    • Quartz Core Additions

      • Dashboard
      • Jobs
      • Serialization (System.Text.Json)
      • Serialization (Newtonsoft Json.NET)
      • Plugins
    • Integrations

      • ASP.NET Core Integration
      • Hosted Services Integration
      • Microsoft DI Integration
      • Multiple Schedulers with Microsoft DI
      • OpenTelemetry Integration
      • OpenTracing Integration
      • Redis Lock Handler
      • TimeZoneConverter Integration
    • 3rd Party Plugins for Quartz
  • Unreleased Releases

    • Quartz 4.x
      • Quartz 4 Quick Start
      • Tutorial
        • Using Quartz
        • Jobs And Triggers
        • More About Jobs & JobDetails
        • More About Triggers
        • Execution Groups
        • Node Affinity (Preferred Node)
        • Simple Triggers
        • Cron Triggers
        • RecurrenceTrigger
        • Trigger and Job Listeners
        • Scheduler Listeners
        • Job Stores
        • Configuration, Resource Usage and SchedulerFactory
        • Advanced (Enterprise) Features
        • Miscellaneous Features
        • CronTrigger Tutorial
      • Configuration Reference
      • JSON Configuration
      • Migration Guide
      • Troubleshooting
      • API Documentation
      • How To's

        • One-Off Job
        • Multiple Triggers
        • Job Template
        • Using the CronTrigger
      • Packages

        • Quartz Core Additions

          • Dashboard
          • Jobs
          • Serialization (System.Text.Json)
          • JSON Serialization
          • Plugins
        • Integrations

          • ASP.NET Core Integration
          • HTTP API
          • Hosted Services Integration
          • Microsoft DI Integration
          • Multiple Schedulers with Microsoft DI
          • OpenTelemetry Integration
          • OpenTracing Integration
          • Redis Lock Handler
          • TimeZoneConverter Integration
        • 3rd Party Plugins for Quartz
  • Old Releases

    • Quartz 2.x
      • Quartz 2 Quick Start
      • Tutorial
        • Lesson 1: Using Quartz
        • Lesson 2: Jobs And Triggers
        • Lesson 3: More About Jobs & JobDetails
        • Lesson 4: More About Triggers
        • Lesson 5: SimpleTrigger
        • Lesson 6: CronTrigger
        • Lesson 7: TriggerListeners and JobListeners
        • Lesson 8: SchedulerListeners
        • Lesson 9: JobStores
        • Lesson 10: Configuration, Resource Usage and SchedulerFactory
        • Lesson 11: Advanced (Enterprise) Features
        • Lesson 12: Miscellaneous Features of Quartz
        • CronTrigger Tutorial
      • Configuration Reference
      • Migration Guide
      • API Documentation
    • Quartz 1.x
      • Tutorial
        • Lesson 1: Using Quartz
        • Lesson 2: Jobs And Triggers
        • Lesson 3: More About Jobs & JobDetails
        • Lesson 4: More About Triggers
        • Lesson 5: SimpleTrigger
        • Lesson 6: CronTrigger
        • Lesson 7: TriggerListeners and JobListeners
        • Lesson 8: SchedulerListeners
        • Lesson 9: JobStores
        • Lesson 10: Configuration, Resource Usage and SchedulerFactory
        • Lesson 11: Advanced (Enterprise) Features
        • Lesson 12: Miscellaneous Features of Quartz
      • API Documentation
  • License

Tips

JSON is recommended persistent format to store data in database for greenfield projects. You should also strongly consider setting useProperties to true to restrict key-values to be strings.

Tips

System.Text.Json serialization is built into the Quartz package and is the default; see Serialization (System.Text.Json).

JSON.NET

Quartz.Serialization.Newtonsoft provides JSON serialization support for job stores using Json.NET to handle the actual serialization process.

Installation

You need to add NuGet package reference to your project which uses Quartz.

Install-Package Quartz.Serialization.Newtonsoft

Configuring

Classic property-based configuration

var properties = new NameValueCollection
{
 ["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
 ["quartz.serializer.type"] = "json"
};
ISchedulerFactory schedulerFactory = new StdSchedulerFactory(properties);

Configuring using scheduler builder

ISchedulerFactory schedulerFactory = QuartzSchedulerBuilder.Create()
    .Configure(q => q.UsePersistentStore(store =>
    {
        store.UseGenericDatabase("MyProvider", "my connection string");

        // it's generally recommended to stick with
        // string property keys and values when serializing
        store.Configure(options => options.UseProperties = true);

        store.UseNewtonsoftJsonSerializer();
    }))
    .Build();

UseGenericDatabase is the right method only for a database Quartz has no specific support for; use UseSqlServer, UsePostgres and the rest otherwise. If Quartz ships no description of your ADO.NET driver either, describe it in the same call — see the configuration reference.

Migrating from binary serialization

Quartz 4 no longer ships the BinaryObjectSerializer: the underlying BinaryFormatter has been removed from modern .NET and throws on .NET 9 and later. If you still have binary-serialized data in your database you need to migrate it to JSON.

The recommended path is to perform the migration while you are still on Quartz 3.x, which still includes BinaryObjectSerializer - see the Quartz 3.x version of this page for a ready-made hybrid serializer. Either let the system migrate gradually as it runs, or write a small program that loads and writes back every serialized asset in the database.

If you must read legacy binary data after upgrading to Quartz 4 on .NET 9 or later, you can re-enable BinaryFormatter with Microsoft's unsupported compatibility package. Because the package does not change BinaryFormatter's type identity, only your application project needs it - Quartz itself does not reference it:

<PropertyGroup>
  <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
<ItemGroup>
  <!-- match the package version to your application's target framework, e.g. 9.0.x on net9.0 -->
  <PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0" />
</ItemGroup>

The package restores a working - but still unsafe - BinaryFormatter, so read the Microsoft guidance before relying on it and remove it once the migration is complete. The Quartz types keep their [Serializable] / ISerializable support, so the hybrid serializer below can read the old binary payloads and write everything back as JSON.

Example hybrid serializer

using System.Runtime.Serialization.Formatters.Binary;

using Newtonsoft.Json;

using Quartz.Simpl;
using Quartz.Spi;

namespace Quartz;

public sealed class MigratorSerializer : IObjectSerializer
{
    // you might need custom configuration, see sections about customizing in documentation
    private readonly NewtonsoftJsonObjectSerializer jsonSerializer = new();

    public void Initialize() => jsonSerializer.Initialize();

    public T DeSerialize<T>(byte[] data) where T : class
    {
        try
        {
            // Attempt to deserialize data as JSON
            return jsonSerializer.DeSerialize<T>(data)!;
        }
        catch (JsonReaderException)
        {
            // The data was not JSON, so fall back to the legacy binary format. This branch needs
            // the System.Runtime.Serialization.Formatters compatibility package and
            // EnableUnsafeBinaryFormatterSerialization to be set in the application project.
            using var stream = new MemoryStream(data);
#pragma warning disable SYSLIB0011
            var binaryData = (T) new BinaryFormatter().Deserialize(stream);
#pragma warning restore SYSLIB0011
            if (binaryData is JobDataMap jobDataMap)
            {
                // make sure we mark the map as dirty so it will be serialized as JSON next time
                jobDataMap[SchedulerConstants.ForceJobDataMapDirty] = "true";
            }
            return binaryData;
        }
    }

    public byte[] Serialize<T>(T obj) where T : class => jsonSerializer.Serialize(obj);
}

Customizing JSON.NET

If you need to customize JSON.NET settings, you need to inherit custom implementation and override CreateSerializerSettings.

class CustomJsonSerializer : NewtonsoftJsonObjectSerializer
{
    protected override JsonSerializerSettings CreateSerializerSettings()
    {
        var settings = base.CreateSerializerSettings();
        settings.Converters.Add(new MyCustomConverter());
        return settings;
    }
}

And then configure it to use

store.UseSerializer<CustomJsonSerializer>();
// or
"quartz.serializer.type" = "MyProject.CustomJsonSerializer, MyProject"

Customizing calendar serialization

If you have implemented a custom calendar, you need to implement a ICalendarSerializer for it. There's a convenience base class CalendarSerializer that you can use the get strongly-typed experience.

Custom calendar and serializer

[Serializable]
class CustomCalendar : BaseCalendar
{
    public CustomCalendar()
    {
    }

    // binary serialization support
    protected CustomCalendar(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        SomeCustomProperty = info?.GetBoolean("SomeCustomProperty") ?? true;
    }

    public bool SomeCustomProperty { get; set; } = true;

    // binary serialization support
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info?.AddValue("SomeCustomProperty", SomeCustomProperty);
    }
}

// JSON serialization support
class CustomCalendarSerializer : CalendarSerializer<CustomCalendar>
{
    protected override CustomCalendar Create(JObject source)
    {
        return new CustomCalendar();
    }

    protected override void SerializeFields(JsonWriter writer, CustomCalendar calendar)
    {
        writer.WritePropertyName("SomeCustomProperty");
        writer.WriteValue(calendar.SomeCustomProperty);
    }

    protected override void DeserializeFields(CustomCalendar calendar, JObject source)
    {
        calendar.SomeCustomProperty = source["SomeCustomProperty"]!.Value<bool>();
    }
}

Configuring custom calendar serializer

var config = SchedulerBuilder.Create();
config.UsePersistentStore(store =>
{
    store.UseNewtonsoftJsonSerializer(json =>
    {
        json.AddCalendarSerializer<CustomCalendar>(new CustomCalendarSerializer());
    });
});

Changed in 4.0

NewtonsoftJsonObjectSerializer.AddCalendarSerializer and AddTriggerSerializer were static in 3.x, so every scheduler in the process shared one set of custom serializers and registration order silently decided which one won. They have been removed. Register through the UseNewtonsoftJsonSerializer callback as above: what the callback registers belongs to that scheduler alone, so two schedulers in one container can serialize different custom types.

If you build a serializer yourself rather than through the store builder, hand it a NewtonsoftJsonSerializerRegistry. A new registry already knows every built-in trigger and calendar type, so registering a custom one adds to that set:

var registry = new NewtonsoftJsonSerializerRegistry()
    .AddCalendarSerializer<CustomCalendar>(new CustomCalendarSerializer())
    .AddTriggerSerializer<CustomTrigger>(new CustomTriggerSerializer());

var serializer = new NewtonsoftJsonObjectSerializer(registry);
serializer.Initialize();
Help us by improving this page!
Last Updated: 7/26/26, 1:18 PM
Contributors: Marko Lahma, Claude Opus 5 (1M context)
Prev
Serialization (System.Text.Json)
Next
Plugins