Как в Elm декодировать объект JSON внутри вложенного JSON

Я использую Elm 0.19.1 с NoRedInk/elm-json-decode-pipeline/1.0.0

У меня есть тип самолета, который

type alias Aircraft = {name:String}

Для этого у меня есть следующий декодер:

aircraftDecoder : Json.Decode.Decoder Aircraft        
aircraftDecoder =            
    Json.Decode.succeed Aircraft            
    |> Json.Decode.Pipeline.required "name" Json.Decode.string

К сожалению, декодер жалуется на то, что я говорю: "BadBody" Проблема с заданным значением: (...)" Это потому, что на самом деле моя интересующая область полна шума вокруг нее (из API HATEOAS). ), как это:

{
  "_embedded" : {
    "aircrafts" : [ {
      "name" : "AC01",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/aircrafts/1"
        },
        "aircraft" : {
          "href" : "http://localhost:8080/aircrafts/1"
        }
      }
    }, {
      "name" : "AC01",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/aircrafts/2"
        },
        "aircraft" : {
          "href" : "http://localhost:8080/aircrafts/2"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/aircrafts{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:8080/profile/aircrafts"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 4,
    "totalPages" : 1,
    "number" : 0
  }
}

Как изменить код и продолжить использовать Pipeline, чтобы декодер не потерялся из-за всего этого шума?

Я что-то слышал об использовании Json.Decode.at, но документации недостаточно, чтобы я мог получить правильный код.


person user5193682    schedule 15.01.2020    source источник


Ответы (2)


Насколько я понимаю, проблема в следующем:

  1. в данный момент ваш декодер вполне может декодировать один Aircraft
  2. однако в вашем JSON есть список Aircraft
  3. кроме того, ваш декодер не знает, где в JSON он может найти Aircrafts

И знаете, для Elm Aircraft и List Aircraft это совершенно разные биты (как и должно быть). В любом случае, решение будет состоять из двух шагов:

  1. Сообщить декодеру, где в структуре JSON находятся Aircrafts
  2. Разберите List Aircraft вместо одного Aircraft

Следуя вашему коду и импортируя at и list из Json.Decode, код может выглядеть так:

listAircraftDecoder : Json.Decode.Decoder List Aircraft 
listAircraftDecoder =
    at ["_embedded", "aircrafts"] (list aircraftDecoder)

Это означает, что теперь мы стремимся к списку Aircrafts и что этот список является массивом в JSON. Начиная с корня JSON, возьмите свойство "_embedded" и внутри него свойство "aircrafts". Это массив, и list в Elm знает, как с ним обращаться.

Наконец, нам просто нужно указать list Elm передать каждый элемент массива JSON определенному декодеру — вашему aircraftDecoder в нашем случае.

Имеет ли это смысл?

person cuducos    schedule 15.01.2020
comment
Очень хорошо!! Это означает, что если бы сам _embedded был внутри _noise, который, в свою очередь, был внутри _root, я должен был использовать at [_root, _noise, _embedded, planes] (список planeDecoder)? То есть я должен указать весь путь от корня до цели? - person user5193682; 15.01.2020
comment
Ага, у тебя получилось! - person cuducos; 15.01.2020

Следующее должно работать:

aircraftNameDecoder : Json.Decode.Decoder String
aircraftNameDecoder =
    Json.Decode.map (Maybe.withDefault "" << List.head) <|
        Json.Decode.at [ "_embedded", "aircrafts" ] <|
            Json.Decode.list (Json.Decode.field "name" Json.Decode.string)


aircraftDecoder : Json.Decode.Decoder Aircraft
aircraftDecoder =
    Json.Decode.succeed Aircraft
        |> Json.Decode.Pipeline.custom aircraftNameDecoder

Дополнительные сведения см. в разделе elm/json. документация по Json.Decode.at.

person simplystuart    schedule 15.01.2020
comment
Привет, просто Стюарт, какова цель Json.Decode.map (Maybe.withDefault ‹‹ List.head)? - person user5193682; 15.01.2020
comment
Насколько я понял, его решение состоит в том, чтобы позволить вам декодировать только первый самолет (List.head), что может быть вашим случаем; ) - person cuducos; 15.01.2020
comment
В вашем объекте над самолетами есть массив, поэтому строка Json.Decode.map преобразует список имен самолетов в одно имя самолета. @cuducos прав, я предположил, что это был ваш случай, основываясь на типе самолета. - person simplystuart; 15.01.2020