2015-12-16 7 views
1

Я пишу клиента RESTeasy Proxy для использования Apple's API для извлечения списка категорий iTunes. При запросе информации о данной категории, например, с этим URL:JAX-RS/Jackson - Deserialize JSON с неизвестным именем элемента корня?

https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres?id=1420 

... вы получите ответ в формате JSON, который выглядит следующим образом:

{ 
    "1420":{ 
     "name":"Self-Help", 
     "id":"1420", 
     "url":"https://itunes.apple.com/us/genre/podcasts-health-self-help/id1420?mt=2", 
     "rssUrls":{ 
     "topVideoPodcastEpisodes":"https://itunes.apple.com/us/rss/topvideopodcastepisodes/genre=1420/json", 
     "topAudioPodcasts":"https://itunes.apple.com/us/rss/topaudiopodcasts/genre=1420/json", 
     "topVideoPodcasts":"https://itunes.apple.com/us/rss/topvideopodcasts/genre=1420/json", 
     "topPodcasts":"https://itunes.apple.com/us/rss/toppodcasts/genre=1420/json", 
     "topAudioPodcastEpisodes":"https://itunes.apple.com/us/rss/topaudiopodcastepisodes/genre=1420/json", 
     "topPodcastEpisodes":"https://itunes.apple.com/us/rss/toppodcastepisodes/genre=1420/json" 
     }, 
     "chartUrls":{ 
     "videoPodcastEpisodes":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=VideoPodcastEpisodes", 
     "podcasts":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=Podcasts", 
     "audioPodcastEpisodes":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=AudioPodcastEpisodes", 
     "audioPodcasts":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=AudioPodcasts", 
     "podcastEpisodes":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=PodcastEpisodes", 
     "videoPodcasts":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=VideoPodcasts" 
     } 
    } 
} 

Я пытаюсь отобразить эту JSON ответ на объект Java с использованием JAXB и Jackson. Тем не менее, «1420» имя корневого элемента, кажется, вызывает проблемы, так как я получаю следующее исключение при вызове моего клиента:

Unrecognized field "1420" (class foo.bar.ITunesCategoryList), not marked as ignorable 

Мой класс JAXB выглядит следующим образом:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class ITunesCategory implements TransferObject { 

    private static final long serialVersionUID = 3443545925023804457L; 

    @XmlElement(name = "id") 
    @JsonProperty("id") 
    private String identifier = null; 

    @XmlElement 
    private String name = null; 

    @XmlElementWrapper(name = "subgenres") 
    private List<ITunesCategory> subcategories = new ArrayList<ITunesCategory>(); 

    ... 
} 

Я даже попытался создать класс-оболочку, поскольку поиск может привести к возврату более чем одной категории. Это выглядит следующим образом:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class ITunesCategoryList implements TransferObject { 

    private static final long serialVersionUID = 3303125979016445238L; 

    @XmlElement 
    private List<ITunesCategory> categories = new ArrayList<ITunesCategory>(); 

    ... 
} 

Однако, независимо от того, какого класса я определяю как мой тип возвращаемого значения, я получаю ту же ошибку, поскольку идентификатор категории является именем корневого элемента объекта JSON.

Есть ли способ сообщить JAXB/Jackson/JAX-RS/RESTeasy игнорировать имя корневого элемента и просто сопоставить базовый объект с Java? Невозможно узнать имя корневого элемента во время разработки/компиляции, так как оно напрямую соответствует результатам, возвращаемым поиском. Есть ли что-то, что можно сделать, чтобы обойти это исключение? Спасибо за любую помощь, которую вы можете дать!

ответ

1

Я не мог найти много динамического игнорирования корня, по крайней мере, не что-то, что было бы подходящим в среде JAX-RS. Единственное, что я мог подумать, это написать собственный десериализатор и просто пропустить корневой узел. Что-то вроде

import com.fasterxml.jackson.core.JsonParser; 
import com.fasterxml.jackson.core.JsonProcessingException; 
import com.fasterxml.jackson.databind.DeserializationContext; 
import com.fasterxml.jackson.databind.JsonDeserializer; 
import com.fasterxml.jackson.databind.JsonNode; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import java.io.IOException; 
import java.util.Map; 

public abstract class IHateRootElemsJsonDeserializer<T> extends JsonDeserializer<T> { 

    private final ObjectMapper mapper = new ObjectMapper(); 
    private final Class<T> cls; 

    public IHateRootElemsJsonDeserializer(Class<T> cls) { 
     this.cls = cls; 
    } 

    @Override 
    public T deserialize(JsonParser jp, DeserializationContext dc) 
      throws IOException, JsonProcessingException { 
     JsonNode rootNode = jp.getCodec().readTree(jp); 
     Map.Entry<String,JsonNode> field = rootNode.fields().next(); 
     JsonNode node = field.getValue(); 
     T result = mapper.convertValue(node, cls); 
     return result; 
    } 
} 

Затем просто протяните его бетонным типом.

public class GenreDeserializer extends IHateRootElemsJsonDeserializer<Genre>{ 

    public GenreDeserializer() { 
     super(Genre.class); 
    } 
} 

Вот тест с использованием точной JSON вы предоставили выше

public class Test { 

    public static void main(String[] args) throws Exception { 
     ObjectMapper mapper = new ObjectMapper(); 
     GenreDeserializer deserializer = new GenreDeserializer(); 
     SimpleModule module = new SimpleModule(); 
     module.addDeserializer(Genre.class, deserializer); 
     mapper.registerModule(module); 

     Genre genre = mapper.readValue(JSON_FILE, Genre.class); 
     System.out.println(genre); 

     genre = mapper.readValue(JSON_FILE, Genre.class); 
     System.out.println(genre); 
    } 

    static final File JSON_FILE = new File("json.json"); 
} 

Рассмотрена модель

public class Genre { 

    public String id; 
    public String name; 
    public String url; 
    public RssUrls rssUrls; 
    public ChartUrls chartUrls; 

    @Override 
    public String toString() { 
     return "Category{" + "id=" + id + ", name=" 
       + name + ", url=" + url + ", rssUrls=" + rssUrls + '}'; 
    } 

    public static class RssUrls { 
     public String topVideoPodcastEpisodes; 
     public String topAudioPodcasts; 
     public String topVideoPodcasts; 
     public String topPodcasts; 
     public String topAudioPodcastEpisodes; 
     public String topPodcastEpisodes; 

     @Override 
     public String toString() { 
      return "RssUrls{" + "topVideoPodcastEpisodes=" + topVideoPodcastEpisodes 
        + ", topAudioPodcasts=" + topAudioPodcasts 
        + ", topVideoPodcasts=" + topVideoPodcasts 
        + ", topPodcasts=" + topPodcasts 
        + ", topAudioPodcastEpisodes=" + topAudioPodcastEpisodes 
        + ", topPodcastEpisodes=" + topPodcastEpisodes + '}'; 
     } 

    } 

    public static class ChartUrls { 
     public String videoPodcastEpisodes; 
     public String podcasts; 
     public String audioPodcastEpisodes; 
     public String audioPodcasts; 
     public String podcastEpisodes; 
     public String videoPodcasts; 

     @Override 
     public String toString() { 
      return "ChatUrls{" + "videoPodcastEpisodes=" + videoPodcastEpisodes 
        + ", podcasts=" + podcasts 
        + ", audioPodcastEpisodes=" + audioPodcastEpisodes 
        + ", audioPodcasts=" + audioPodcasts 
        + ", podcastEpisodes=" + podcastEpisodes 
        + ", videoPodcasts=" + videoPodcasts + '}'; 
     } 
    } 
} 

Чтобы настроить ObjectMapper в JAX-RS, вы можете посмотреть на this post

+0

Спасибо! Я обязательно посмотрю на это и сделаю это! – Shadowman

+0

Работал как шарм! Благодаря! – Shadowman

Смежные вопросы