Table of Contents

Bodu.Text.DotEnv

Bodu.Text.DotEnv

Bodu.Text.DotEnv reads and writes .env files - a flat object of KEY=value entries with optional export prefixes, single- and double-quoted values, and # comments - as a standalone System.Text.Json-shaped library: a forward-only ref struct reader/writer pair over UTF-8 bytes, a typed settings serializer, an export-flag-preserving mutable DOM, and a read-only document DOM. It is one of the three Bodu line formats and shares the Bodu.Text.Serialization attribute, naming-policy, and callback vocabulary with every other Bodu serializer.

Values are deliberately literal - no ${VAR} interpolation happens at parse time - and the wire is string-only, so the serializer converts scalars with InvariantCulture at the binding layer. The Web preset applies the SCREAMING_SNAKE_CASE naming policy with case-insensitive matching, so conventional APP_PORT-style keys bind onto PascalCase members without a single [PropertyName].

Part of the Text & Serialization topic.

Install

dotnet add package Bodu.Text.DotEnv

Targets net8.0. Depends on Bodu.Text.Serialization and Bodu.Core. Also available through the Bodu.Text.Formats umbrella package.

Headline types

Type Purpose
Utf8DotEnvReader / Utf8DotEnvWriter Forward-only, allocation-free token machines, configured by DotEnvReaderOptions / DotEnvWriterOptions.
DotEnvSerializer Settings POCO or Dictionary<string, string> ↔ text: Serialize / Deserialize<T> over strings, UTF-8 spans, buffer writers, and streams (with buffered async variants).
DotEnvSerializerOptions / DotEnvSerializerDefaults Naming policy, case sensitivity, IncludeFields, DefaultIgnoreCondition, WriteExportPrefix; General / Web presets.
DotEnvNode / DotEnvObject / DotEnvValue Mutable DOM that preserves each entry's export flag through a round trip.
DotEnvDocument / DotEnvElement / DotEnvProperty Read-only, disposable document model.
DotEnvFormatException / DotEnvSerializationException Malformed input (unterminated quote, missing =, with position) vs a value that cannot bind.

A first parse and bind

using System.Text;
using Bodu.Text.DotEnv;
using Bodu.Text.DotEnv.Document;

public sealed class Settings
{
    public string? AppEnv { get; set; }       // APP_ENV
    public int AppPort { get; set; }          // APP_PORT
    public string? DatabaseUrl { get; set; }  // DATABASE_URL
}

const string env = "# service settings\nexport APP_ENV=production\nAPP_PORT=8080\nDATABASE_URL=\"postgres://db.example.com/app\"\n";

using (DotEnvDocument doc = DotEnvDocument.Parse(Encoding.UTF8.GetBytes(env)))
{
    string url = doc.RootElement.GetProperty("DATABASE_URL").GetString();   // postgres://db.example.com/app
}

Settings settings = DotEnvSerializer.Deserialize<Settings>(env, new DotEnvSerializerOptions(DotEnvSerializerDefaults.Web));
// settings.AppEnv → "production", settings.AppPort → 8080

Where to go next