# Codec with non-matching structures: object/custom type

**URL:** <https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121>\
**Category:** Learn\
**Created:** [May 3, 2023, 8:35am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121 "2023-05-03T08:35:44Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![sylbru](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sylbru/32/3570_2.png) [@sylbru](https://discourse.elm-lang.org/u/sylbru)\
**Post date:** [May 3, 2023, 8:35am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/1 "2023-05-03T08:35:45Z")

</div>

I have a type that I currently have encoders/decoders for. I want to turn them into `elm-codec`s but I’m really struggling.

Here’s the Elm type:

```elm
type D
    = A { f : Int }
    | B { b : String }
    | C { f : Int }

```

And here’s the JSON structure :

```json
{"type": "A", "f": 123}
or
{"type": "B", "b": "blah"}
or
{"type": "C", "f": 456}

```

Here’s how it works currently (on the decoding side):

- a `Decode.oneOf` tries `aDecoder`, `bDecoder` and `cDecoder`
- each of those checks the fields it needs, and then (`Decode.andThen`) asserts that the `type` field has the right value.

This bit with the “type” being a _field_ of the data object in the JSON, while being a _variant_ in the Elm type, I can’t seem to find a way to make it translate to `elm-codec`. Is that just normal, that the codec technique requires you to have similar structures on the Elm and JSON sides? Or is there a way? (I tried something with `Codec.andThen` but got nowhere.)

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [May 3, 2023, 9:00am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/2 "2023-05-03T09:00:37Z")

</div>

[https://package.elm-lang.org/packages/miniBill/elm-codec/latest/Codec#custom](https://package.elm-lang.org/packages/miniBill/elm-codec/latest/Codec#custom)

I think the choice of field that is used to determine the variant (“type” in your case) is hard coded into elm-codec? Looking at the code I think it might be “tag” but I didn’t manage to fully grok it yet.

> <https://github.com/miniBill/elm-codec/blob/2.0.0/src/Codec.elm#L515>

---

<div class="post-metadata">

**Author:** ![sylbru](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sylbru/32/3570_2.png) [@sylbru](https://discourse.elm-lang.org/u/sylbru)\
**Post date:** [May 3, 2023, 9:03am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/3 "2023-05-03T09:03:37Z")

</div>

Yes it gives something like `{"tag":"…","args":[…]}`.  
I was wondering if there was a more advanced way to get around it, maybe redesigning my type differently. (But that would mean quite a bit of refactoring now, and I might not have enough time budget on this project!)

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [May 3, 2023, 9:04am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/4 "2023-05-03T09:04:50Z")

</div>

If you already have a decoder and encoder you can build a Codec out of them by using:

[https://package.elm-lang.org/packages/miniBill/elm-codec/latest/Codec#build](https://package.elm-lang.org/packages/miniBill/elm-codec/latest/Codec#build)

---

<div class="post-metadata">

**Author:** ![sylbru](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sylbru/32/3570_2.png) [@sylbru](https://discourse.elm-lang.org/u/sylbru)\
**Post date:** [May 3, 2023, 9:07am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/5 "2023-05-03T09:07:12Z")

</div>

Interesting! But what is the point of using a Codec if I’m building it out of two potentially out-of-sync (en|de)coders?

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [May 3, 2023, 9:20am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/6 "2023-05-03T09:20:22Z")

</div>

You might just be building part of a larger Codec, where most of it is standard elm-codec, but you need just a few parts completely custom. If elm-codec gives you nothing, then don’t use it and stick with standard encoder+decoder.

---

<div class="post-metadata">

**Author:** ![sylbru](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sylbru/32/3570_2.png) [@sylbru](https://discourse.elm-lang.org/u/sylbru)\
**Post date:** [May 3, 2023, 9:40am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/7 "2023-05-03T09:40:01Z")

</div>

Right, I understand. I thought about sticking to elm-codec just for the record part (the variants’ argument). That’s what I started with, the only problem I have is that I don’t know how to/if I can reproduce this constraint on the value of the `type` field.

I can write this:

```auto
Codec.object
  (\type f -> { f = f }) 
  |> Codec.field "type" (always "A") Codec.string
  |> Codec.field "f" .f Codec.int
  |> Codec.buildObject  

```

Encoding works fine, and puts the type in there, but decoding ignores the `type` field, so it works even in `type` is different (which defeats the purpose of this field, which is to disambiguate between different types of data which might use the same fields).

Here is an Ellie that I just put together that shows it all:

- encoding is successful
- decoding from the previously encoded string is successful, but…
- decoding is also successful if the `type` field is nonsense

[https://ellie-app.com/mHSVMp22sj7a1](https://ellie-app.com/mHSVMp22sj7a1) (fixed, again!)

---

<div class="post-metadata">

**Author:** ![sylbru](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sylbru/32/3570_2.png) [@sylbru](https://discourse.elm-lang.org/u/sylbru)\
**Post date:** [May 3, 2023, 11:58am UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/8 "2023-05-03T11:58:52Z")

</div>

Woops that Ellie link was nonsense, here’s the right one: [https://ellie-app.com/mHSVMp22sj7a1](https://ellie-app.com/mHSVMp22sj7a1)

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [May 3, 2023, 3:09pm UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/9 "2023-05-03T15:09:33Z")

</div>

miniBill/elm-codec is very opinionated when it comes to what the JSON looks like. We ran into problems with that, just like you do, when trying to introduce it at work. Partly because we had to support already existing JSON structures, partly because we read the actual JSON a lot in Postgres and having to do `'args'->0` all the time there was annoying.

So we forked elm-codec into our own version where you can customize the “tag” field name, and put data in whatever fields you like (instead of `"args"`) and a bunch of other little tweaks and changes (like better error messages). I can’t share that code though.

---

<div class="post-metadata">

**Author:** ![sylbru](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sylbru/32/3570_2.png) [@sylbru](https://discourse.elm-lang.org/u/sylbru)\
**Post date:** [May 3, 2023, 3:15pm UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/10 "2023-05-03T15:15:58Z")

</div>

Ok I guess I’m in that sort of case then. It’s not worth the time for me to find a better solution for this specific problem, so I guess that’s it. Thank you both for your help!

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [May 3, 2023, 3:34pm UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/11 "2023-05-03T15:34:15Z")

</div>

> [@lydell](#):
>
> So we forked elm-codec into our own version where you can customize the “tag” field name, and put data in whatever fields you like (instead of `"args"`) and a bunch of other little tweaks and changes (like better error messages). I can’t share that code though.

Something like that might make a nice PR against elm-codec.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [May 13, 2023, 3:35pm UTC](https://discourse.elm-lang.org/t/codec-with-non-matching-structures-object-custom-type/9121/12 "2023-05-13T15:35:07Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
