ralfstx / minimal-json

A fast and small JSON parser and writer for Java
MIT License
736 stars 185 forks source link

jsonvalue.isInteger() #24

Open 239 opened 10 years ago

239 commented 10 years ago

Why are methods like isInteger() missing? Are simplicity and minimalism the reasons? Right now there is only isNumber() so I use

jsonvalue.toString().contains(".")

additionally as a workaround to check whether the number is an "integer" or not.

ralfstx commented 10 years ago

Would a method isInteger be supposed to indicate whether

(a) the value is a number without fractional part (as opposed to isFloatingPoint)

This would include numbers that are out of range, the implementation would probably only check for the existence of a dot.

(b) the value fits into a Java integer (as opposed to isLong, isFloat, etc)

This would include values like 1e3 that cannot be parsed by Integer.parseInt(). In order to provide those methods, minimal-json would have to implement parsing algorithms for different number types.

(c) the value can be parsed as int

Since parsing is the responsibility of the parser, this option doesn't seem to make a lot of sense.

239 commented 10 years ago

Since (c) implies (a) and (b) I assume it would need to indicate both cases. Using minimal-json values it's easy to check the value type before getting the value by using isObject()/isString()/... so you can then use asObject()/asString()/.... But how to check properly whether the value can be transformed to integer, long, float or double if isNumber() is true in all cases? Would this be a better workaround?

if (v.isNumber()) {
    Long l = null;
    Double d = null;
    try {
        l = v.asLong();
    } catch (NumberFormatException e) {
        d = v.asDouble();
    }
}
ralfstx commented 10 years ago

It seems that you are trying to transform JSON into a corresponding Java model without knowing the "metamodel", i.e. the expected format for each key. You don't use the values right away (otherwise you'd know which type to expect). Instead you store them for later use, e.g. in a hash map. By doing so, you discard type information and need an instanceof switch again to process the values when you look them up later.

Instead of transforming values back and forth, how about keeping them as JsonValue? In fact, JsonValue is exactly this: a Java model that represents the parsed JSON without taking any information away.

239 commented 10 years ago

As I commented in #25 I have a simple JSON editor that creates a JSpinner for number values with a SpinnerNumberModel according to the "number type":

SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
SpinnerNumberModel(double value, double minimum, double maximum, double stepSize)

After editing, the new value from the JSpinner replaces the old value so I can't see a way to avoid transforming it. Although it's not a full transformation since the JSON value is kept all the time and only updated if the user wants to change it. There is no intanceof involved thanks to the is...() checking.

239 commented 10 years ago

Maybe a corresponding asNumber() method would fit better into the current system?

public Number asNumber() { return Double.parseDouble(string); }

A) If I do know what number type to expect from a value then I also could use:

Number n = v.asNumber();
int i = n.intValue(); / long l = n.longValue(); / double d = n.doubleValue();

B) If I don't know what type to expect then I still can use any of the methods above since they involve rounding or truncation when needed without worrying about exceptions.

Of course it would not solve my problem but I also don't see the meaning of isNumber() in conjunction with asInt(), asLong(), asFloat() and asDouble(): 1) If a certain number type for a value is expected then there is no need for isNumber(). 2) If the value type is not known then isNumber() tells me only that it probably can be transformed by asDouble() if the value is in double range. But then it's the same as v.asNumber().doubleValue().

ralfstx commented 10 years ago

Good points. I agree that a method Number asNumber() would fit better with isNumber(). We could parse numbers as BigDecimal which extends Number. asInt(), asDouble() etc. could remain as shortcuts for asNumber().intValue(), asNumber().doubleValue() etc.

239 commented 10 years ago

Yes, BigDecimal is even better suited for JSON numbers. Using lazy loading to avoid repeated parsing?

private BigDecimal number;
public Number asNumber() { return number == null ? number = new BigDecimal(string) : number; }
ralfstx commented 10 years ago

I thought about that too. The idea of minimal-json is to parse as fast as possible and to keep the footprint of the Java representation as small as possible. So yes, for the sake of parsing speed, number parsing should be done on demand when asNumber() is called. Since BigDecimal is immutable, the value could be cached and returned again. This would add at least the reference to the BigInteger the footprint of the number class, even if the value is still null. I'm not sure if this is worthwhile - however, I think I'll do some experiments and measurements over the weekend...