docs: private static Object jsonBasicValueToObject(final Object jsonVal, final Type expectedType,
              final boolean convertStringToNumber) {
          if (jsonVal == null) {
              return null;
          } else if (jsonVal instanceof JSONArray || jsonVal instanceof JSONObject) {
              throw ClassGraphException.newClassGraphException("Expected a basic value type");
          }
          if (expectedType instanceof ParameterizedType) {
              // TODO: add support for Class<T> reference values, which may be parameterized
              throw new IllegalArgumentException("Got illegal ParameterizedType: " + expectedType);
          } else if (!(expectedType instanceof Class<?>)) {
              throw new IllegalArgumentException("Got illegal basic value type: " + expectedType);
          }
  
          final Class<?> rawType = (Class<?>) expectedType;
          if (rawType == String.class) {
              if (!(jsonVal instanceof CharSequence)) {
                  throw new IllegalArgumentException("Expected string; got " + jsonVal.getClass().getName());
              }
              return jsonVal.toString();
  
          } else if (rawType == CharSequence.class) {
              if (!(jsonVal instanceof CharSequence)) {
                  throw new IllegalArgumentException("Expected CharSequence; got " + jsonVal.getClass().getName());
              }
              return jsonVal;
  
          } else if (rawType == Integer.class || rawType == Integer.TYPE) {
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return Integer.parseInt(jsonVal.toString());
              }
              if (!(jsonVal instanceof Integer)) {
                  throw new IllegalArgumentException("Expected integer; got " + jsonVal.getClass().getName());
              }
              return jsonVal;
  
          } else if (rawType == Long.class || rawType == Long.TYPE) {
              final boolean isLong = jsonVal instanceof Long;
              final boolean isInteger = jsonVal instanceof Integer;
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return isLong ? Long.parseLong(jsonVal.toString()) : Integer.parseInt(jsonVal.toString());
              }
              if (!(isLong || isInteger)) {
                  throw new IllegalArgumentException("Expected long; got " + jsonVal.getClass().getName());
              }
              if (isLong) {
                  return jsonVal;
              } else {
                  return (long) (Integer) jsonVal;
              }
  
          } else if (rawType == Short.class || rawType == Short.TYPE) {
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return Short.parseShort(jsonVal.toString());
              }
              if (!(jsonVal instanceof Integer)) {
                  throw new IllegalArgumentException("Expected short; got " + jsonVal.getClass().getName());
              }
              final int intValue = (Integer) jsonVal;
              if (intValue < Short.MIN_VALUE || intValue > Short.MAX_VALUE) {
                  throw new IllegalArgumentException("Expected short; got out-of-range value " + intValue);
              }
              return (short) intValue;
  
          } else if (rawType == Float.class || rawType == Float.TYPE) {
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return Float.parseFloat(jsonVal.toString());
              }
              if (!(jsonVal instanceof Double)) {
                  throw new IllegalArgumentException("Expected float; got " + jsonVal.getClass().getName());
              }
              final double doubleValue = (Double) jsonVal;
              if (doubleValue < Float.MIN_VALUE || doubleValue > Float.MAX_VALUE) {
                  throw new IllegalArgumentException("Expected float; got out-of-range value " + doubleValue);
              }
              return (float) doubleValue;
  
          } else if (rawType == Double.class || rawType == Double.TYPE) {
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return Double.parseDouble(jsonVal.toString());
              }
              if (!(jsonVal instanceof Double)) {
                  throw new IllegalArgumentException("Expected double; got " + jsonVal.getClass().getName());
              }
              return jsonVal;
  
          } else if (rawType == Byte.class || rawType == Byte.TYPE) {
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return Byte.parseByte(jsonVal.toString());
              }
              if (!(jsonVal instanceof Integer)) {
                  throw new IllegalArgumentException("Expected byte; got " + jsonVal.getClass().getName());
              }
              final int intValue = (Integer) jsonVal;
              if (intValue < Byte.MIN_VALUE || intValue > Byte.MAX_VALUE) {
                  throw new IllegalArgumentException("Expected byte; got out-of-range value " + intValue);
              }
              return (byte) intValue;
  
          } else if (rawType == Character.class || rawType == Character.TYPE) {
              if (!(jsonVal instanceof CharSequence)) {
                  throw new IllegalArgumentException("Expected character; got " + jsonVal.getClass().getName());
              }
              final CharSequence charSequence = (CharSequence) jsonVal;
              if (charSequence.length() != 1) {
                  throw new IllegalArgumentException("Expected single character; got string");
              }
              return charSequence.charAt(0);
  
          } else if (rawType == Boolean.class || rawType == Boolean.TYPE) {
              if (convertStringToNumber && jsonVal instanceof CharSequence) {
                  return Boolean.parseBoolean(jsonVal.toString());
              }
              if (!(jsonVal instanceof Boolean)) {
                  throw new IllegalArgumentException("Expected boolean; got " + jsonVal.getClass().getName());
              }
              return jsonVal;
          } else if (Enum.class.isAssignableFrom(rawType)) {
              if (!(jsonVal instanceof CharSequence)) {
                  throw new IllegalArgumentException(
                          "Expected string for enum value; got " + jsonVal.getClass().getName());
              }
              @SuppressWarnings({ "rawtypes", "unchecked" })
              final Enum enumValue = Enum.valueOf((Class<Enum>) rawType, jsonVal.toString());
              return enumValue;
  
          } else if (JSONUtils.getRawType(expectedType).isAssignableFrom(jsonVal.getClass())) {
              return jsonVal;
  
          } else {
              throw new IllegalArgumentException("Got type " + jsonVal.getClass() + "; expected " + expectedType);
          }
      }