process part of json file

184 Views Asked by At

I've got a json file that looks like this

{
  "races": [
    {
      "name"  : "ORC"
    },
    {
      "name"  : "HUMAN"
    },
    {
      "name"  : "ELF"
    }
  ],
  "npc": [
    {
      "race"  : "HUMAN",
      "age"   : "25",
      "name"  : "Jerome"
    },
    {
      "race"  : "ORC",
      "age"   : "26",
      "name"  : "Rz'Ul"
    }
  ]
}

I want to retrieve the data from races or npc separately on demand. I am using genson to parse JSON. I parse it like this

@SuppressWarnings("unchecked")
public static <T> List<T> readJsonList(String listName) {
    Genson genson = JsonContext.PARSER.getParser();
    List<T> result = null;
    try (InputStream is = JsonDataProcessor.class.getResourceAsStream(FILE)) {
        ObjectReader reader = genson.createReader(is);
        reader.beginObject();
        while (reader.hasNext()) {
            reader.next();

            if ("races".equals(listName) && "races".equals(reader.name())) {
                result = (List<T>) processRaceData(reader);
                break;
            }

            if ("npc".equals(listName) && "npc".equals(reader.name())) {
                result = (List<T>) processNpcData(reader);
                break;
            }

        }
        reader.endObject();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return result;
}

And for example the method to parse races looks like this

private static List<Race> processRaceData(ObjectReader reader) {
    List<Race> raceList = new ArrayList<>();
    reader.beginArray();
    while (reader.hasNext()) {
        reader.next();

        Race race = new Race();
        reader.beginObject();
        while (reader.hasNext()) {
            reader.next();

            if ("name".equals(reader.name())) { race.setName(reader.valueAsString()); }
            else { reader.skipValue(); }
        }
        reader.endObject();
        raceList.add(race);
    }
    reader.endArray();
    return raceList;
}

In debug it populates variable result just fine, but on endObject line I get exception

Exception in thread "main" com.owlike.genson.stream.JsonStreamException: Illegal character at row 11 and column 4 expected } but read ',' !
        at com.owlike.genson.stream.JsonReader.newWrongTokenException(JsonReader.java:942)
        at com.owlike.genson.stream.JsonReader.end(JsonReader.java:428)
        at com.owlike.genson.stream.JsonReader.endObject(JsonReader.java:177)
        at com.lapots.breed.platform.json.JsonDataProcessor.readJsonList(JsonDataProcessor.java:41)
        at com.lapots.breed.platform.Example.prepareDb(Example.java:18)
        at com.lapots.breed.platform.Example.main(Example.java:23)

What is the problem?

2

There are 2 best solutions below

3
On BEST ANSWER

I played with your code and found that genson is expecting to close object and check } symbol when you call endObject. You just need to skip object with reader.skipValue(). So error is in your while block in readJsonList method. This piece of code should work good for you:

 @SuppressWarnings("unchecked")
 public static <T> List<T> readJsonList(String listName) {
 . . .
 while (reader.hasNext()) {
     reader.next();
     if ("races".equals(listName) && "races".equals(reader.name())) {
         result = result = (List<T>) processRaceData(reader);
         break;
     }
     if ("npc".equals(listName) && "npc".equals(reader.name())) {
         result = result = (List<T>) processNpcData(reader);
         break;
     }
     reader = reader.skipValue();
 }
 reader.endObject();
 . . .
0
On

Why don't you use the data-binding capabilities of Genson and let it do the skipValue and mapping for you? You could define two data structures, one for races and another one for npc and then deserialize to them like this:

class Races {
  public List<Race> races;
}

class Npcs {
  public List<Npc> npc;
}

// This will skip npc and all the fields in it
Races races = genson.deserialize(inputStream, Races.class);

This remains pretty efficient and less error prone than writing the deserialization using the low level streaming API.