Преобразовать поле JSON в вариант ReasonML

У меня есть структура JSON, которая содержит поле period это может быть либо объект, либо строка. У меня уже есть готовый вариант в моем коде, и он работает нормально:

type period = {
  start: string,
  end_: string,
};

type periodVariant =
  | String(string)
  | Period(period);

Проблема в том, что я пытаюсь привести входной JSON к типу варианта: я просто не знаю, как это сделать. Вот как выглядит моя попытка:

let periodVariantDecode = (json: Js.Json.t): periodVariant => {
  switch(json) {
  | String(string) => String(Json.Decode.string(string))
  | period => Period(Json.Decode.{
      start: period |> field("start", string),
      end_: period |> field("end", string),
    })
  };
};

Теперь, конечно, это не работает, потому что я пытаюсь сопоставить что-то, что по-прежнему типа Js.Json.t против String которая является частью моего periodVariant, но я не знаю, как добиться того, чего я хочу.

2 ответа

Решение

Это то, что either для. Вместе с map удобно "поднять" существующий декодер на ваш тип варианта.

type period = {
  start: string,
  end_:  string,
};

type periodVariant =
  | String(string)
  | Period(period);

let period = json =>
  Json.Decode.{
    start: json |> field("start", string),
    end_:  json |> field("end", string),
  };

let periodVariantDecode =
  Json.Decode.(either(
    period |> map(p => Period(p)),
    string |> map(s => String(s))
  ));

Я вижу, вы используете bs-json, поэтому один из способов сделать это - воспользоваться тем, что Json.Decode.optional возвращается None если декодирование не удается. Для вашего примера:

type period = {
  start: string,
  end_: string,
};

type periodVariant =
  | String(string)
  | Period(period);

let periodVariantDecode = json => {
  let periodString = json |> Json.Decode.(optional(string));
  switch (periodString) {
  | Some(periodString) => String(periodString)
  | None =>
    let periodObj =
      Json.Decode.{
        start: json |> field("start", string),
        end_: json |> field("end", string),
      };
    Period(periodObj);
  };
};

Это должно скомпилировать с periodVariantDecode быть типом Js.Json.t => periodVariant, Я не уверен, если это идиоматический способ пойти, хотя!

Другие вопросы по тегам