-1

We are doing a text-based adventure where you read in the layout from a JSON file. I am reading in a JSON file to create a Layout class which contains room objects. Each room has a map of directions which maps the direction name (ie south, east, west, etc) to the direction object. Inside the layout class, there is another map that maps the room names to the room object. When I run my code, the debugger tells me that my roomMap as well as my directionMap are both null. So when I try to call a JUnit test to make sure that the proper value is returned when I access the maps, I get a NullPointerException.

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;

public class Room {

// instance variables
protected String name;
protected String description;
protected Item[] items;
protected Direction[] directions;

protected ArrayList<Direction> testList = new ArrayList<>();
protected HashMap<String, Direction> directionMap = new HashMap<>();

public Room(String name, String description, Direction[] directions, Item[] items) {
    this.name = name;
    this.description = description;
    this.items = items;
    this.directions = directions;
    for (Direction d : directions) {
        directionMap.put(d.directionName, d);
    }
}

}

This is my direction class:

package com.example;

import com.sun.org.apache.xpath.internal.operations.Bool;

public class Direction {

// instance variables
protected String room;
protected String directionName;
protected boolean enabled;
protected String[] validKeyNames;


public Direction(String room, String directionName, String enabled, String[] validKeyNames) {
    this.room = room;
    this.directionName = directionName;
    this.enabled = Boolean.parseBoolean(enabled);
    this.validKeyNames = validKeyNames;
}


public static boolean compareDirections (Direction d1, Direction d2){
    boolean roomMatch = d1.room.equals(d2.room);

    boolean nameMatch = d1.directionName.equals(d2.directionName);

    boolean enabledMatch = d1.enabled == d2.enabled;

    boolean validKeyNamesMatch = true;

    if(d1.validKeyNames.length == d2.validKeyNames.length){
        for(int i = 0; i < d1.validKeyNames.length; i++){
            if(!d1.validKeyNames[i].equals(d2.validKeyNames[i])){
                validKeyNamesMatch = false;
            }
        }
    }

    return roomMatch && nameMatch && enabledMatch && validKeyNamesMatch;
}

}

This is my Player class:

public class Player {

protected Item[] items;
protected Room currentRoom;

public Player(Item[] items, Room startingRoom){
    this.items = items;
    this.currentRoom = startingRoom;
}

}

This is my Items class:

public class Item {
protected String name;

public Item(String name){
    this.name = name;
}

public static boolean compareItems (Item i1, Item i2){
    return i1.name.equals(i2.name);
}

}

And this is my layout class where roomMap is

public class Layout {

// instance variables
protected String startingRoom;
protected String endingRoom;
protected Room[] rooms;
protected Player player;

protected HashMap<String, Room> roomMap = new HashMap<>();


public Layout(String startingRoom, String endingRoom, Room[] rooms, Player player) {
    this.startingRoom = startingRoom;
    this.endingRoom = endingRoom;
    this.rooms = rooms;
    this.player = player;

    for (Room room : rooms) {
        roomMap.put(room.name, room);
    }
}

This is the JSON reader class:

public class JsonReader {

public static String getJsonFromUrl (String link) throws IOException {
    try{
        URL url = new URL(link);
    } catch(MalformedURLException e){
        System.out.println("That is not a valid link");
        System.exit(0);
    }
    URL url = new URL(link);
    // Connect to the URL using java's native library
    URLConnection request = url.openConnection();
    request.connect();

    // Convert to a JSON object to print data
    JsonParser jp = new JsonParser(); //from gson
    JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent())); //Convert the input stream to a json element
    JsonObject rootobj = root.getAsJsonObject(); //May be an array, may be an object.
    return rootobj.toString();
}

}

I have tested the Gson parser and know for a fact that all the values from the JSON file are being properly serialized, which is why I'm confused that the key-value pair are not being put inside the hashmap.

Here are my JUnit Tests

import com.google.gson.Gson;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.IOException;

import static org.junit.Assert.*;

public class AdventureTest {


Adventure adventure;

@Before
public void setup() {
    adventure = new Adventure("https://drive.google.com/uc?export=download&id=1qmmrC3jZ3zrmdvrL8ukM5r4pB4BYhfyd");
}

@Test
public void getJsonFromUrlTest() throws IOException {
    assertEquals(
            "{\"startingRoom\":\"SDRPCommons\",\"endingRoom\":\"HopkinsRoom345\",\"rooms\":[{\"name\":\"SDRPCommons\",\"description\":\"You are in the Ikenberry Dining Hall. There is good food, good people, and good study areas\",\"directions\":[{\"directionName\":\"North\",\"room\":\"DiningHall\",\"enabled\":\"false\",\"validKeyNames\":[\"I-card\"]},{\"directionName\":\"Northwest\",\"room\":\"UpstairsArea\",\"enabled\":\"true\",\"validKeyNames\":[]},{\"directionName\":\"West\",\"room\":\"OutsideAreaOfSixPack\",\"enabled\":\"false\",\"validKeyNames\":[\"backpack\",\"depression\"]}],\"items\":[{\"name\":\"I-card\"},{\"name\":\"Chair\"},{\"name\":\"Fork\"}]},{\"name\":\"DiningHall\",\"description\":\"The best eats on campus, other than PAR/FAR, Chomps, and basically anything on Green Street.\",\"directions\":[{\"directionName\":\"South\",\"room\":\"SDRPCommons\",\"enabled\":\"true\",\"validKeyNames\":[\"depression\"]}],\"items\":[{\"name\":\"sweatshirt\"},{\"name\":\"key\"}]},{\"name\":\"UpstairsArea\",\"description\":\"You are upstairs. Near you is Caffeinator but it's closed. So there is nothing to do but to go back to your room and grind...\",\"directions\":[{\"directionName\":\"Southeast\",\"room\":\"SDRPCommons\",\"enabled\":\"true\",\"validKeyNames\":[]}],\"items\":[{\"name\":\"depression\"},{\"name\":\"backpack\"}]},{\"name\":\"OutsideAreaOfSixPack\",\"description\":\"It's a beautiful day and everyone is out and about. Kinda makes you want to sit in your room in the dark, right?\",\"directions\":[{\"directionName\":\"North\",\"room\":\"HopkinsRoom345\",\"enabled\":\"false\",\"validKeyNames\":[\"depression\",\"backpack\"]}],\"items\":[{\"name\":\"depression\"},{\"name\":\"backpack\"}]},{\"name\":\"HopkinsRoom345\",\"description\":\"This is your room. Go inside and grind your cs 126 hw, idiot.\",\"directions\":[{\"directionName\":\"South\",\"room\":\"OutsideAreaOfSixPack\",\"enabled\":\"false\",\"validKeyNames\":[\"key\",\"depression\"]}],\"items\":[{\"name\":\"bed\"}]}]}",
            JsonReader.getJsonFromUrl("https://drive.google.com/uc?export=download&id=1qmmrC3jZ3zrmdvrL8ukM5r4pB4BYhfyd")
    );
}

@Test
public void loadJsonIntoLayout() throws IOException {
    String json = JsonReader.getJsonFromUrl("https://drive.google.com/uc?export=download&id=1qmmrC3jZ3zrmdvrL8ukM5r4pB4BYhfyd");
    Gson gson = new Gson();
    Layout layout = gson.fromJson(json, Layout.class);
    if (layout != null) {
        assertEquals("SDRPCommons", layout.startingRoom);

        //  System.out.println(layout.roomMap.get(layout.startingRoom));
        Room r = adventure.roomMap.get("DiningHall");
        Direction d = adventure.directionMap.get("south");
        String s = d.directionName;
        assertEquals("south", layout.roomMap.get("DiningHall").directionMap.get("south"));
    }
}

}

This is the adventure class:

import com.google.gson.Gson;
import java.io.*;
import java.lang.reflect.Array;
import java.net.*;
import java.util.*;

import static sun.plugin.javascript.navig.JSType.Element;

public class Adventure {

protected Layout layout;
protected HashMap<String, Room> roomMap = new HashMap<>();
protected HashMap<String, Direction> directionMap = new HashMap<>();
//    private Player player;
private static String jsonUrl;
// link for easy access: https://drive.google.com/uc?export=download&id=1qmmrC3jZ3zrmdvrL8ukM5r4pB4BYhfyd
protected Room currentRoom;

protected final List<String> ACTION_VERBS = Arrays.asList("go");
protected final List<String> EXIT_VERBS = Arrays.asList("exit", "quit");
protected final List<String> ITEM_VERBS = Arrays.asList("pickup", "use");


public Adventure(String url) {
    this.jsonUrl = url;
    this.layout = getLayoutFromJson(jsonUrl);
    String startingRoom = layout.getStartingRoom();
    for (Room r : layout.rooms) {
        for (Direction d : r.directions) {
            r.directionMap.put(d.directionName, d);
        }
        layout.roomMap.put(r.name, r);
    }
    this.currentRoom = roomMap.get(startingRoom);
}

public Layout getLayoutFromJson(String url) {
    try {
        String json = JsonReader.getJsonFromUrl(url);
        Gson gson = new Gson();
        Layout layout = gson.fromJson(json, Layout.class);
        return layout;
    } catch (Exception e) {
        return null;
    }
}

}

  • 3
    Hmm. The classes look fine as you show them. You should show something that we can run so that we can check that we have the same issue you have. So, if you made JUnit tests and they see a NullPointerException, you should show these JUnit tests. – kumesana Sep 19 '18 at 08:13
  • With the given information, only 2 possibilities that I can see: 1) Room and Layout constructors were not called 2) rooms and directions arrays were empty – mdewit Sep 19 '18 at 08:19
  • Any reason you are using `protected` access specifier? Share your JUnit tests as well – Nicholas K Sep 19 '18 at 08:31
  • I chose protected because these variables need to be accessed by other classes in the same package. Also,I just posted the adventure class – advai podduturi Sep 19 '18 at 08:37
  • Dude. The whole point is that we run the same thing as you run so that we check that we have the same issue. So you need to show all your classes, and all the code of all your classes. Frankly there is too much code here. If you have a problem, you should start with removing everything which is not linked to the problem. So, please do that, and come back with a question on a problem you made simpler, as should always be done as a first step. – kumesana Sep 19 '18 at 08:43
  • Class Layout is not complete. And I have already told you that the first step you need to take to solve your problem, is to remove all the code that isn't linked to the problem so that it becomes much easier to focus on what could be going wrong. You do not seem to be removing any code, and therefore doing your work on the problem. I don't think it can be solved if you won't do the needed work on it. – kumesana Sep 19 '18 at 09:14
  • just a hint: JSON is not part of standard java, and probably part of the problem - what Library/framework is being used for JSON is important part of the question (at least as a tag) – user85421 Sep 19 '18 at 09:30

1 Answers1

1

Gson is not going to invoke a non-default constructor automatically. Either it invokes a no-args constructor or it will "automagically" create an instance out of nothing and then populate its fields via reflection. Read Is default no-args constructor mandatory for Gson?

That means your roomMap/directionMap population code is not going to be invoked as that code is inside non-default constructors.

gpeche
  • 21,974
  • 5
  • 38
  • 51