MMazen AlsenihSenior full stack developer
ExperienceExpertiseProductsBlogContact
View CV

Mazen Alsenih

Building clear, resilient software for complex environments.

Germany-based

Navigate

ExperienceExpertiseBlogProducts

Connect

LinkedInGitHubXingX

© 2026 Mazen Alsenih. All rights reserved.

Privacy policy
Elwood: A Purpose-Built Language for JSON Transformation

Field note

Elwood: A Purpose-Built Language for JSON Transformation

A practical look at Elwood, a functional JSON transformation language designed for readable pipelines and consistent behavior across .NET and TypeScript.

June 3, 2026

JSON transformation often begins as a small mapping function and gradually becomes a maintenance problem.

An upstream payload contains fields the application does not need. The downstream system expects a different structure. Then filtering, conditional values, lookups, date formatting, and null handling are added. Before long, the intent of the transformation is hidden inside loops, object construction, and defensive code.

My manager and friend Max, Massimiliano Favilli, encountered this problem repeatedly while building extended JSON parsers. As the syntax and use cases grew, the implementation became increasingly difficult to evolve.

That experience led him to create Elwood, a domain-specific language focused entirely on JSON transformation.

The design goal

Elwood is functional and expression-oriented. It combines JSONPath-style navigation with pipes, named lambdas, pattern matching, bindings, and a standard library of transformation functions.

The following example selects confirmed orders, reshapes them, sorts them, and returns the first ten:

The important characteristic is readability. Data moves from left to right, and each stage communicates one decision. The expression describes the result more directly than an equivalent collection of loops and temporary objects.

Why use a domain-specific language?

Handwritten TypeScript or C# can perform every transformation shown here. The question is not whether a general-purpose language is capable. It is whether it is the best representation for logic that changes independently from the application.

A transformation language can be useful when:

  • Mapping rules are stored as configuration
  • Integrations need to change without redeploying the host application
  • The same rules must run in different technology stacks
  • Non-core transformation logic should remain isolated from domain code
  • Operators need to inspect or test mappings independently

The trade-off is equally important. A DSL introduces another language to learn and another runtime to govern. It should earn that complexity through clearer logic, safer configuration, or better portability.

Pipes and named lambdas

The pipe operator is the foundation of Elwood:

Each operator receives the output of the previous stage. Named lambdas make the current value explicit, which is particularly helpful once a transformation contains nested expressions.

This style keeps selection, projection, ordering, and limiting visible as separate operations. A reviewer can follow the data flow without mentally executing control structures.

Bindings for multi-step transformations

Complex mappings benefit from named intermediate results:

Bindings prevent a large expression from becoming a puzzle. They also give business concepts meaningful names, which makes the transformation easier to discuss and test.

Pattern matching

Pattern matching expresses conditional mapping without nested ternary expressions:

The fallback arm makes the default behavior explicit. That is useful in integration code, where unexpected values are normal and must have a defined outcome.

Memoized lookup functions

Reference lookups are common in transformation pipelines. Repeating the same lookup for every input item can be expensive and obscure the intent.

Memoization caches results by argument. The transformation remains declarative while avoiding repeated evaluation for duplicate identifiers.

Lazy evaluation

The .NET implementation can evaluate pipelines lazily. Operators stream values instead of materialising every intermediate collection.

That behavior matters when a pipeline ends with an operation such as take 10. The engine can stop once it has enough matching records instead of transforming the entire input first.

Performance claims should always be tested against representative data, but the execution model is appropriate for large payloads and early-termination scenarios.

Using Elwood from TypeScript

The core package can be installed from npm:

The host application provides an expression and an input object, then receives the evaluated result.

Using Elwood from .NET

The .NET packages expose the same language model:

The .NET and TypeScript implementations are validated against a shared conformance suite. That shared behavior is one of the strongest aspects of the project. A transformation tested in one supported runtime should produce the same result in the other.

CLI and service integration

For local testing and one-off transformations, the CLI supports standard input, script files, and an interactive environment:

Elwood can also be hosted behind an HTTP API:

That option allows systems outside .NET and Node.js to use the same transformation language through a controlled service boundary.

Where it fits

The clearest use cases are integration-heavy:

ETL and data exchange. Source and destination schemas differ, and mappings need to remain visible and changeable.

Webhook normalisation. Third-party payloads can be converted into a stable internal contract at the ingestion boundary.

API response shaping. Data from several services can be projected into a client-specific response without adding mapping code throughout the application.

Configurable business mappings. Transformation rules can be stored and versioned separately from the host service.

I would not use Elwood for general application logic, network orchestration, or stateful workflows. Its strength comes from having a narrow purpose.

My assessment

The most convincing aspect of Elwood is not the number of language features. It is the consistency of the mental model.

Navigation identifies the input. Pipes show the direction of data. Lambdas name the current item. Pattern matching makes branches visible. Bindings divide complex logic into understandable stages.

That combination allows a transformation to communicate intent before implementation detail. It also makes the logic easier to review with people who understand the data flow but do not need to know the host application's internal architecture.

Elwood will not replace every mapping function, and it should not. For transformations that need to be portable, configurable, or independently maintained, it offers a thoughtful alternative.

The fastest way to evaluate whether it fits your own work is to try a representative payload in the Elwood playground and compare the result with the code you maintain today.

Mazen Alsenih

Written by

Mazen Alsenih

Back to blog posts
#Elwood#JSON#Data Transformation#DSL#.NET#TypeScript