Croparia IF Docs

|

General

Section
Developers
10 TOP-LEVEL ITEMS
    Developers
    Core Modules

      Crop Transmuter

    Codec API

MultiCodec

MultiCodec<T> lets one data type support several serialized forms. It tries its inner codecs in order and returns the first successful result.

This need appears all the time in vanilla-style data systems, but Mojang does not provide a ready-made "ordered union codec." Croparia IF's MultiCodec fills exactly that gap.

Core idea

MultiCodec<T> is essentially an ArrayList<TestedCodec<? extends T>> that also implements Codec<T> itself.

Its behavior is:

  1. try the first codec
  2. if it fails, record the error
  3. try the next codec
  4. continue until one succeeds
  5. if all fail, merge their errors and return the combined failure

So the most important thing is not just that it can hold multiple codecs, but that their order matters.

Earlier branches get the first chance to consume the input, so you should think carefully about:

  • which format is more specific
  • which format is broader
  • whether the failure messages will still help during debugging

When to use it

Common use cases:

  • one value may be written as a single value or as a list
  • one object may use a shorthand form or a full object form
  • old and new config formats need to coexist

For example, a field may accept:

"minecraft:stone"

or:

["minecraft:stone", "minecraft:dirt"]

That is exactly the kind of case MultiCodec is good at.

Basic usage

The most common constructor path is CodecUtil.of(...):

MultiCodec<List<String>> codec = CodecUtil.of(
    Codec.STRING.listOf(),
    Codec.STRING.xmap(List::of, List::getFirst)
);

This means:

  • first try to decode the input as a list
  • if that fails, try it as a single string
  • normalize both forms into List<String>

In day-to-day code, it is often better to use CodecUtil.listOf(...) directly, because that helper already captures the "single item or list" pattern cleanly.

Relationship with TestedCodec

MultiCodec only decides ordering. It does not decide whether a branch is sensible for the current input. That part belongs to TestedCodec.

Inside CodecUtil.of(...), plain codecs are wrapped into TestedCodec automatically, so you can choose between:

  • passing plain codecs
  • passing TestedCodec branches that already have custom filtering

When branch boundaries are easy to describe, explicit TestedCodec checks usually make the result:

  • easier to debug
  • safer against overly broad branches
  • more stable for long-term format compatibility

CodecUtil.listOf(...)

CodecUtil.listOf(codec) is the most common MultiCodec helper. It lets one value accept:

  • a single element
  • a list of elements

and always returns List<E>.

MultiCodec<List<Identifier>> codec = CodecUtil.listOf(Identifier.CODEC);

That codec can accept:

"croparia:gem_earth"

and:

["croparia:gem_earth", "croparia:gem_water"]

The point is not just saving a few lines. It is about making a very common compatibility pattern behave the same way everywhere.

Choosing branch order

Branch order is the main design choice in MultiCodec. A good default rule is:

  • put more specific formats first
  • put broader, easier-to-misfire formats later

For example:

  • an object form is usually more specific than a plain string form
  • a list form is usually more specific than a single-value form

If the order is reversed, a loose branch may succeed too early and prevent the better branch from ever running.

Tips

  • MultiCodec is best for "one meaning, several spellings." It is not a replacement for normal data modeling.
  • If the number of branches keeps growing, it is often better to narrow the model first instead of stacking more alternatives.
  • When one branch shape is easy to detect, combine MultiCodec with TestedCodec.
  • If your only need is "single value or list," prefer CodecUtil.listOf.
In This Page
MultiCodec
NO EXTRACTED HEADINGS