class JsonUtil {

  static DatanodeInfo toDatanodeInfo(final Map<?, ?> m) 
    throws IOException {
    if (m == null) {
      return null;
    }

    // ipAddr and xferPort are the critical fields for accessing data.
    // If any one of the two is missing, an exception needs to be thrown.

    // Handle the case of old servers (1.x, 0.23.x) sending 'name' instead
    //  of ipAddr and xferPort.
    String ipAddr = getString(m, "ipAddr", null);
    int xferPort = getInt(m, "xferPort", -1);
    if (ipAddr == null) {
      String name = getString(m, "name", null);
      if (name != null) {
        int colonIdx = name.indexOf(':');
        if (colonIdx > 0) {
          ipAddr = name.substring(0, colonIdx);
          xferPort = Integer.parseInt(name.substring(colonIdx +1));
        } else {
          throw new IOException(
              "Invalid value in server response: name=[" + name + "]");
        }
      } else {
        throw new IOException(
            "Missing both 'ipAddr' and 'name' in server response.");
      }
      // ipAddr is non-null & non-empty string at this point.
    }

    // Check the validity of xferPort.
    if (xferPort == -1) {
      throw new IOException(
          "Invalid or missing 'xferPort' in server response.");
    }

    // TODO: Fix storageID
    return new DatanodeInfo(
        ipAddr,
        (String)m.get("hostName"),
        (String)m.get("storageID"),
        xferPort,
        (int)(long)(Long)m.get("infoPort"),
        getInt(m, "infoSecurePort", 0),
        (int)(long)(Long)m.get("ipcPort"),

        getLong(m, "capacity", 0l),
        getLong(m, "dfsUsed", 0l),
        getLong(m, "remaining", 0l),
        getLong(m, "blockPoolUsed", 0l),
        getLong(m, "cacheCapacity", 0l),
        getLong(m, "cacheUsed", 0l),
        getLong(m, "lastUpdate", 0l),
        getInt(m, "xceiverCount", 0),
        getString(m, "networkLocation", ""),
        AdminStates.valueOf(getString(m, "adminState", "NORMAL")));
  }

}