简体   繁体   中英

Parsing a malformed(?) json file with Jackson Json

I have a JSON file from MTG JSON that is setup like this:

{
  "data": {
    "10E": {
      ...
    }
    "2ED": {
      ...
    }
    "3ED": {
      ...
    }
    ...
  }
}

Each of the objects under data is of type Set , but they're not labeled as Set , instead they're labeled like you see above -- with a three letter set-code.

I know I can do something like this:

public class Data {
  @JsonProperty("10E")
  Set x10E;
  
  @JsonProperty("2ED")
  Set x2ED;
}

but I don't want to have to hardcode each set by name. Instead, I'd like to have an array or list of sets. Something like this, where Jackson ignores the label and treats it like an array (or any other list) of Set Objects.

public class Data {
  Set[] sets;
}

Is there a way to get Jackson JSON to make that conversion? My current main method looks like this:

public class AllPrintings {
  Data data;
  public static void main(String[] args) throws Exception {
    byte[] jsonData = Files.readAllBytes(Paths.get("stage/AllPrintings.json"));
    JsonParser parser = new ObjectMapper().readTree(jsonData).traverse();
    ObjectMapper om = new ObjectMapper();
    parser.nextToken();
    om.setVisibility(VisibilityChecker.Std.defaultInstance().withFieldVisibility(Visibility.ANY));
    AllPrintings allPrintings = om.readValue(parser, AllPrintings.class);
  }
}

As recommended by @Tom in the comments, I can use the annotation @JsonAnySetter to dump any undefined fields into a Map:

public class Data {
  Map<String, Set> sets = new HashMap<>();
  
  @JsonAnySetter
  public void setOtherField(String name, Set value) {
    sets.put(name, value);
  }
}

To start, Set is the name of a standard built-in Java type , so I'll use MtgSet to avoid confusion from using an existing name.

It looks like you may be parsing JSON:API , in which case a library that handles its structure specifically might be helpful. However, I'll show how to do it in just plain Jackson.

To start, we'll define a wrapper class to match your root JSON:

class AllPrintings {
  Map<String, MtgSet> data;
}

Your Jackson invocations are a little overcomplicated, and we can simplify.

var is = new FileInputStream("stage/AllPrintings.json"); // just open it directly
Map<String, MtgSet> sets = new ObjectMapper()
  .readValue(is, AllPrintings.class) // no need to explicitly mess with parser instances
  .data; // pull the wrapped value out of the wrapper class
is.close();

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