Skip to content

Converters

Domain Services uses System.Text.Json. Many domain models rely on custom JSON converters for correct serialization/deserialization (polymorphism, value objects, strong-typed IDs, etc.).

Each Web API module ships its own set of converters exposed via a standard entry point named:

{Module}.WebApi.SerializerOptionsDefault.Options.Converters

Because of this, when you add a module (TimeSeries, GIS, Jobs, Places, Meshes, …) you must also register that module’s converters in Program.cs.


Base setup (from the project template)

The BaseWebApi template already wires up the core converters from DHI.Services:

// MVC
builder.Services
    .AddResponseCompression(options => { options.EnableForHttps = true; })
    .AddControllers()
    .AddJsonOptions(options =>
    {
        // Configure JSON serialization options
        options.JsonSerializerOptions.WriteIndented = true;
        options.JsonSerializerOptions.DefaultIgnoreCondition
            = SerializerOptionsDefault.Options.DefaultIgnoreCondition;
        options.JsonSerializerOptions.PropertyNamingPolicy
            = SerializerOptionsDefault.Options.PropertyNamingPolicy;

        // Core DS converters (from DHI.Services / DHI.Services.WebApi)
        options.JsonSerializerOptions
               .AddConverters(SerializerOptionsDefault.Options.Converters);

#warning Depending on which Web API packages you install in this project,
#warning you need to register domain-specific JSON converters for these packages
    });

The last warning is intentional: it reminds you to add converters for each domain-specific Web API you install.


Add converters for each Web API module

Example: TimeSeries Web API

options.JsonSerializerOptions.AddConverters(
    DHI.Services.TimeSeries.WebApi.SerializerOptionsDefault.Options.Converters
);

Example: multiple modules (Places, GIS, Meshes)

options.JsonSerializerOptions
    .AddConverters(DHI.Services.Places.WebApi.SerializerOptionsDefault.Options.Converters);
options.JsonSerializerOptions
    .AddConverters(DHI.Services.GIS.WebApi.SerializerOptionsDefault.Options.Converters);
options.JsonSerializerOptions
    .AddConverters(DHI.Services.Meshes.WebApi.SerializerOptionsDefault.Options.Converters);

You can add as many as needed—one line per module.


Important notes

  • Namespace/assembly matters. Every module exposes SerializerOptionsDefault in its own WebApi assembly/namespace. Make sure you reference the correct one, e.g. DHI.Services.TimeSeries.WebApi.SerializerOptionsDefault, not just DHI.Services.TimeSeries.SerializerOptionsDefault.
  • Order is fine-grained-safe. Converters are appended; typical modules don’t conflict, but if you define your own custom converters for the same types, add yours after the module converters to override behavior.
  • Swagger & clients. Once registered, Swagger will show correct request/response shapes. If you are writing clients that use System.Text.Json, mirror the same converter registration on the client’s JsonSerializerOptions to deserialize responses correctly.
  • When you’ll notice missing converters:

    • NotSupportedException: Deserialization of types deriving from … is not supported
    • Unexpected null values or default values after model binding
    • Polymorphic payloads losing concrete type information

Quick checklist when adding a module

  1. Install the Web API package (e.g., DHI.Services.TimeSeries.WebApi).
  2. In Program.cs, add its converter line under .AddJsonOptions(...).
  3. Run and hit the new endpoints in Swagger; verify JSON round-trips.

Summary

  • Converters are per-module: each Web API package ships its own SerializerOptionsDefault.Options.Converters.
  • Add one AddConverters(...) call per module — they stack without conflict.
  • Always reference the WebApi assembly namespace (e.g., DHI.Services.TimeSeries.WebApi.SerializerOptionsDefault), not the domain assembly.

Next: Dynamic Connections