简体   繁体   中英

Parsing dynamically generated JSON object names with Jackson

I'm attempting to deserialize some MediaWiki context from JSON using Jackson into POJOs. However, the problem is that one of the JSON object names is the integer ID value of the article, so using an annotation like @JsonProperty can't be used because the value is never constant.

Here's some sample JSON to describe what I mean:

http://en.wikipedia.org/w/api.php?action=query&titles=Albert%20Einstein&prop=info&format=json&indexpageids

{
    "query": {
        "pageids": [
            "736"
        ],
        "pages": {
            "736": {
                "pageid": 736,
                "ns": 0,
                "title": "Albert Einstein",
                "contentmodel": "wikitext",
                "pagelanguage": "en",
                "touched": "2014-01-05T03:14:23Z",
                "lastrevid": 588780054,
                "counter": "",
                "length": 106159
            }
        }
    }
}

(MediaWiki recommends adding the &indexpageids parameter to assist with parsing, however I can't see how it would be useful to me.)

I tried using the @JsonAnyGetter and @JsonAnySetter annotations as well but they don't appear to help, throwing the same exception com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "736" (class WikipediaPages), not marked as ignorable (one known property: "wikipediaPage"]) .

Thanks for any and all assistance.

Edit: Here's what the relevant classes look like at the moment:

public class WikipediaPages {

    private Map<String, WikipediaPage> wikipediaPageMap = new HashMap<String, WikipediaPage>();

    public Map<String, WikipediaPage> getWikipediaPageMap() {
        return wikipediaPageMap;
    }

    public void setWikipediaPageMap(Map<String, WikipediaPage> wikipediaPageMap) {
        this.wikipediaPageMap = wikipediaPageMap;
    }
}

I use a Jackson Mixin to apply annotations:

public interface WikipediaPagesMixIn {

    @JsonAnyGetter
    Map<String, WikipediaPage> getWikipediaPageMap();

    @JsonAnySetter
    void setWikipediaPageMap(Map<String, WikipediaPage> wikipediaPageMap);
}

Edit 2: More code, as requested:

public class JacksonBuilder {

    private static ObjectMapper objectMapper;

    public static ObjectMapper getObjectMapper() {
        if(objectMapper == null) {
            objectMapper = new ObjectMapper();
            objectMapper.registerModule(new WikipediaModule());
        }

        return objectMapper;
    }
}

public class WikipediaModule extends SimpleModule {

    public WikipediaModule() {
        super("WikipediaModule", new Version(1, 0, 0, null, "net.ryanmorrison", "sentience"));
    }

    @Override
    public void setupModule(SetupContext setupContext) {
        setupContext.setMixInAnnotations(WikipediaPage.class, WikipediaPageMixIn.class);
        setupContext.setMixInAnnotations(WikipediaPages.class, WikipediaPagesMixIn.class);
        setupContext.setMixInAnnotations(WikipediaQuery.class, WikipediaQueryMixIn.class);
        setupContext.setMixInAnnotations(WikipediaResult.class, WikipediaResultMixIn.class);
    }
}

public class WikipediaResult {

    private WikipediaQuery wikipediaQuery;

    public WikipediaQuery getWikipediaQuery() {
        return wikipediaQuery;
    }

    public void setWikipediaQuery(WikipediaQuery wikipediaQuery) {
        this.wikipediaQuery = wikipediaQuery;
    }
}

public interface WikipediaResultMixIn {

    @JsonProperty("query")
    WikipediaQuery getWikipediaQuery();
}

To answer the root cause of your exception, the @JsonAnySetter javadoc states

Marker annotation that can be used to define a non-static, two-argument method (first argument name of property, second value to set) , [...]

As such, using a mixin like this

@JsonAnySetter
void setWikipediaPageMap(Map<String, WikipediaPage> wikipediaPageMap);

doesn't register it and therefore the property isn't found.

Honestly, don't use mixins if you control the data classes. You can directly map the fields as I've shown below.


I don't know how you are using your mixin, but the following works for me

String json = "{ \"query\": { \"pageids\": [ \"736\" ], \"pages\": { \"736\": { \"pageid\": 736, \"ns\": 0, \"title\": \"Albert Einstein\", \"contentmodel\": \"wikitext\", \"pagelanguage\": \"en\", \"touched\": \"2014-01-05T03:14:23Z\", \"lastrevid\": 588780054, \"counter\": \"\", \"length\": 106159 } } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode node =mapper.readTree(json);

node = node.get("query").get("pages");

Map<String, Page> pages = mapper.readValue(node.traverse(), new TypeReference<Map<String, Page>>() {
});

System.out.println(pages);

prints

{736=Page [pageid=736, ns=0, title=Albert Einstein, contentmodel=wikitext, pagelanguage=en, touched=2014-01-05T03:14:23Z, lastrevid=588780054, counter=, length=106159]}

Where Page is

class Page {
    private int pageid;
    private int ns;
    private String title;
    private String contentmodel;
    private String pagelanguage;
    private String touched; // this could be a Date, with the appropriate format configuration
    private int lastrevid;
    private String counter;
    private int length;
    @Override
    public String toString() {
        return "Page [pageid=" + pageid + ", ns=" + ns + ", title=" + title
                + ", contentmodel=" + contentmodel + ", pagelanguage="
                + pagelanguage + ", touched=" + touched + ", lastrevid="
                + lastrevid + ", counter=" + counter + ", length=" + length
                + "]";
    }
    public int getPageid() {
        return pageid;
    }
    public void setPageid(int pageid) {
        this.pageid = pageid;
    }
    public int getNs() {
        return ns;
    }
    public void setNs(int ns) {
        this.ns = ns;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContentmodel() {
        return contentmodel;
    }
    public void setContentmodel(String contentmodel) {
        this.contentmodel = contentmodel;
    }
    public String getPagelanguage() {
        return pagelanguage;
    }
    public void setPagelanguage(String pagelanguage) {
        this.pagelanguage = pagelanguage;
    }
    public String getTouched() {
        return touched;
    }
    public void setTouched(String touched) {
        this.touched = touched;
    }
    public int getLastrevid() {
        return lastrevid;
    }
    public void setLastrevid(int lastrevid) {
        this.lastrevid = lastrevid;
    }
    public String getCounter() {
        return counter;
    }
    public void setCounter(String counter) {
        this.counter = counter;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
}

All that is left is to put the Map<String, Page> as a field in some wrapper class for the query and pages JSON elements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM