767 lines
24 KiB
C#
767 lines
24 KiB
C#
// JValue - JNumber
|
|
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using Leguar.TotalJSON.Internal;
|
|
|
|
namespace Leguar.TotalJSON {
|
|
|
|
/// <summary>
|
|
/// Class to store number value in JSON format. Once JNumber instance is created, its value can't be changed.
|
|
///
|
|
/// There is no limit how long or big numbers can be, but trying to read too big number for example as long will cause exception.
|
|
/// Oversized numbers can be still read out and handled as strings.
|
|
/// </summary>
|
|
public class JNumber : JValue {
|
|
|
|
private readonly string valueAsString;
|
|
|
|
/// <summary>
|
|
/// Creates new JSON number value from string. There is no limits in number size as long as it follows json number format.
|
|
/// </summary>
|
|
/// <param name="numberAsString">
|
|
/// Value for this JNumber object in string format.
|
|
/// </param>
|
|
/// <exception cref="JArgumentException">
|
|
/// If parameter string is not valid number.
|
|
/// </exception>
|
|
public JNumber(string numberAsString) : base() {
|
|
string errorMessage=numberFormatCheck(numberAsString);
|
|
if (errorMessage!=null) {
|
|
throw (new JArgumentException("Parameter \""+numberAsString+"\" is not valid JSON format number: "+errorMessage,"numberAsString"));
|
|
}
|
|
valueAsString = numberAsString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates new JSON number value from c# int value.
|
|
/// </summary>
|
|
/// <param name="numberAsInt">
|
|
/// Value for this JNumber object.
|
|
/// </param>
|
|
public JNumber(int numberAsInt) : base() {
|
|
valueAsString=safeLongAsString(numberAsInt);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates new JSON number value from c# long value.
|
|
/// </summary>
|
|
/// <param name="numberAsLong">
|
|
/// Value for this JNumber object.
|
|
/// </param>
|
|
public JNumber(long numberAsLong) : base() {
|
|
valueAsString=safeLongAsString(numberAsLong);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates new JSON number value from c# float value.
|
|
/// </summary>
|
|
/// <param name="numberAsFloat">
|
|
/// Value for this JNumber object.
|
|
/// </param>
|
|
/// <exception cref="JArgumentException">
|
|
/// If parameter is NaN or Infinity.
|
|
/// </exception>
|
|
public JNumber(float numberAsFloat) : base() {
|
|
if (float.IsNaN(numberAsFloat)) {
|
|
throw (new JArgumentException("Can not create new JNumber from float that is NaN","numberAsFloat"));
|
|
}
|
|
if (float.IsPositiveInfinity(numberAsFloat)) {
|
|
throw (new JArgumentException("Can not create new JNumber from float that is infinity","numberAsFloat"));
|
|
}
|
|
if (float.IsNegativeInfinity(numberAsFloat)) {
|
|
throw (new JArgumentException("Can not create new JNumber from float that is negative infinity","numberAsFloat"));
|
|
}
|
|
valueAsString=safeFloatAsString(numberAsFloat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates new JSON number value from c# double value.
|
|
/// </summary>
|
|
/// <param name="numberAsDouble">
|
|
/// Value for this JNumber object.
|
|
/// </param>
|
|
/// <exception cref="JArgumentException">
|
|
/// If parameter is NaN or Infinity.
|
|
/// </exception>
|
|
public JNumber(double numberAsDouble) : base() {
|
|
if (double.IsNaN(numberAsDouble)) {
|
|
throw (new JArgumentException("Can not create new JNumber from double that is NaN","numberAsDouble"));
|
|
}
|
|
if (double.IsPositiveInfinity(numberAsDouble)) {
|
|
throw (new JArgumentException("Can not create new JNumber from double that is infinity","numberAsDouble"));
|
|
}
|
|
if (double.IsNegativeInfinity(numberAsDouble)) {
|
|
throw (new JArgumentException("Can not create new JNumber from double that is negative infinity","numberAsDouble"));
|
|
}
|
|
valueAsString=safeDoubleAsString(numberAsDouble);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates new JSON number value from c# decimal value.
|
|
/// </summary>
|
|
/// <param name="numberAsDecimal">
|
|
/// Value for this JNumber object.
|
|
/// </param>
|
|
public JNumber(decimal numberAsDecimal) : base() {
|
|
valueAsString=addDecimalPoint(numberAsDecimal.ToString(CultureInfo.InvariantCulture));
|
|
}
|
|
|
|
private JNumber(string safeValueAsString, ParseStringRunner parseStringRunner) : base() {
|
|
valueAsString=parseStringRunner.getPossiblyFixedNumber(safeValueAsString);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns compact information of this JNumber object as string, for debug purposes.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Single string with information of this JNumber object.
|
|
/// </returns>
|
|
public override string ToString() {
|
|
return ("[JNumber: "+valueAsString+"]");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test if another object equals to this object. Always returns false if parameter object is null or it is not instance of JNumber.
|
|
/// Two JNumber objects are equal if both contains value which string representation is exactly equal.
|
|
/// For example JNumber that contains "1" is not equal to JNumber that contains "1.0"
|
|
/// </summary>
|
|
/// <param name="anotherObject">
|
|
/// Another object that is compared to this one.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if objects are equal, false otherwise.
|
|
/// </returns>
|
|
public override bool Equals(object anotherObject) {
|
|
if (anotherObject==null) {
|
|
return false;
|
|
}
|
|
if (!(anotherObject is JNumber)) {
|
|
return false;
|
|
}
|
|
JNumber anotherJNumber=(JNumber)(anotherObject);
|
|
return (valueAsString.Equals(anotherJNumber.AsString()));
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return valueAsString.GetHashCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as long. This will throw exception if number is floating point number or outside long range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as long.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in long.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public long AsLong() {
|
|
try {
|
|
return long.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"long",""+long.MinValue,""+long.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"long"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as unsigned long. This will throw exception if number is floating point number or outside ulong range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as ulong.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in ulong.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public ulong AsULong() {
|
|
try {
|
|
return ulong.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"ulong",""+ulong.MinValue,""+ulong.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"ulong"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as int. This will throw exception if number is floating point number or outside int range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as int.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in int.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public int AsInt() {
|
|
try {
|
|
return int.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"int",""+int.MinValue,""+int.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"int"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as unsigned int. This will throw exception if number is floating point number or outside uint range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as uint.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in uint.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public uint AsUInt() {
|
|
try {
|
|
return uint.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"uint",""+uint.MinValue,""+uint.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"uint"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as short. This will throw exception if number is floating point number or outside short range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as short.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in short.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public short AsShort() {
|
|
try {
|
|
return short.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"short",""+short.MinValue,""+short.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"short"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as unsigned short. This will throw exception if number is floating point number or outside ushort range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as ushort.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in ushort.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public ushort AsUShort() {
|
|
try {
|
|
return ushort.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"ushort",""+ushort.MinValue,""+ushort.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"ushort"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as byte. This will throw exception if number is floating point number or outside byte range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as byte.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in byte.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public byte AsByte() {
|
|
try {
|
|
return byte.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"byte",""+byte.MinValue,""+byte.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"byte"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as signed byte. This will throw exception if number is floating point number or outside sbyte range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as sbyte.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in sbyte.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber is floating point number.
|
|
/// </exception>
|
|
public sbyte AsSByte() {
|
|
try {
|
|
return sbyte.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"sbyte",""+sbyte.MinValue,""+sbyte.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString,"sbyte"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as double. This will throw exception if number is outside double range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as double.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in double.
|
|
/// </exception>
|
|
public double AsDouble() {
|
|
try {
|
|
double value=double.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
if (double.IsInfinity(value)) {
|
|
throw (new OverflowException());
|
|
}
|
|
return value;
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"double",""+double.MinValue,""+double.MaxValue));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as float. This will throw exception if number is outside float range.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as float.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in float.
|
|
/// </exception>
|
|
public float AsFloat() {
|
|
try {
|
|
float value=float.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
if (float.IsInfinity(value)) {
|
|
throw (new OverflowException());
|
|
}
|
|
return value;
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"float",""+float.MinValue,""+float.MaxValue));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number object as decimal. This will throw exception if number is outside decimal range or number contains E/e notation.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as decimal.
|
|
/// </returns>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in decimal.
|
|
/// </exception>
|
|
/// <exception cref="JNumberFormatException">
|
|
/// If number stored to this JNumber uses E/e notation (like 1.234567e89)
|
|
/// </exception>
|
|
public decimal AsDecimal() {
|
|
try {
|
|
return decimal.Parse(valueAsString,CultureInfo.InvariantCulture);
|
|
}
|
|
catch (OverflowException) {
|
|
throw (new JNumberOverflowException(valueAsString,"decimal",""+decimal.MinValue,""+decimal.MaxValue));
|
|
}
|
|
catch (FormatException) {
|
|
throw (new JNumberFormatException(valueAsString));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number as string.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Value as string.
|
|
/// </returns>
|
|
public string AsString() {
|
|
return valueAsString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of this number as object. First fitting value of these are returned: int, long, float, double
|
|
/// </summary>
|
|
/// <exception cref="JNumberOverflowException">
|
|
/// If number stored to this JNumber doesn't fit in double.
|
|
/// </exception>
|
|
/// <returns>
|
|
/// Value as object, that may be one of the 4 basic number objects.
|
|
/// </returns>
|
|
public object AsObject() {
|
|
int iValue;
|
|
if (int.TryParse(valueAsString, NumberStyles.Integer, CultureInfo.InvariantCulture, out iValue)) {
|
|
return iValue;
|
|
}
|
|
long lValue;
|
|
if (long.TryParse(valueAsString, NumberStyles.Integer, CultureInfo.InvariantCulture, out lValue)) {
|
|
return lValue;
|
|
}
|
|
float fValue;
|
|
if (float.TryParse(valueAsString, NumberStyles.Float, CultureInfo.InvariantCulture, out fValue)) {
|
|
if (!float.IsInfinity(fValue)) {
|
|
return fValue;
|
|
}
|
|
}
|
|
// This could fail too in extreme cases, but custom exception is thrown
|
|
return this.AsDouble();
|
|
}
|
|
|
|
internal override void zCreate(CreateStringRunner createStringRunner) {
|
|
createStringRunner.appendColoring(CreateStringRunner.COLOR_NUMBER);
|
|
createStringRunner.append(this.AsString());
|
|
createStringRunner.appendColoring(CreateStringRunner.COLOR_END);
|
|
}
|
|
|
|
internal static JNumber zParse(ParseStringRunner parseStringRunner, char firstChr) {
|
|
|
|
StringPointer sp = parseStringRunner.getStringPointer();
|
|
|
|
int start=sp.getCurrentIndex()-1; // -1 since first character is already read
|
|
bool valid=parseCheck(sp,firstChr);
|
|
if (!valid) {
|
|
throw ParseException.forInvalidCharacter("Invalid number value \""+sp.getSubStringStartingFrom(start)+"\"",parseStringRunner);
|
|
} else {
|
|
sp.stepBack();
|
|
string validNumber=sp.getSubStringStartingFrom(start);
|
|
return (new JNumber(validNumber,parseStringRunner));
|
|
}
|
|
|
|
}
|
|
|
|
private static bool parseCheck(StringPointer sp, char chr) {
|
|
int state;
|
|
if (chr=='-') {
|
|
chr=sp.getNextChar();
|
|
}
|
|
if (chr=='0') {
|
|
chr=sp.getNextChar();
|
|
if (chr=='.') {
|
|
state=5;
|
|
} else if (chr=='e' || chr=='E') {
|
|
state=7;
|
|
} else {
|
|
return true; // (-)0
|
|
}
|
|
} else if (chr>='1' && chr<='9') {
|
|
state=4;
|
|
} else {
|
|
return false;
|
|
}
|
|
do {
|
|
chr=sp.getNextChar();
|
|
if (state==4) {
|
|
if (chr>='0' && chr<='9') {
|
|
state=4;
|
|
} else if (chr=='.') {
|
|
state=5;
|
|
} else if (chr=='e' || chr=='E') {
|
|
state=7;
|
|
} else {
|
|
return true; // (-)##
|
|
}
|
|
} else if (state==5) {
|
|
if (chr>='0' && chr<='9') {
|
|
state=6;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (state==6) {
|
|
if (chr>='0' && chr<='9') {
|
|
state=6;
|
|
} else if (chr=='e' || chr=='E') {
|
|
state=7;
|
|
} else {
|
|
return true; // (-)(##).##
|
|
}
|
|
} else if (state==7) {
|
|
if (chr=='+' || chr=='-') {
|
|
chr=sp.getNextChar();
|
|
if (chr>='0' && chr<='9') {
|
|
state=9;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (chr>='0' && chr<='9') {
|
|
state=9;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (state==9) {
|
|
if (chr>='0' && chr<='9') {
|
|
state=9;
|
|
} else {
|
|
return true; // (-)(##).##[e/E](+/-)##
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
private static string numberFormatCheck(string str) {
|
|
|
|
int count=str.Length,
|
|
state=1;
|
|
|
|
for (int index=0; index<count; index++) {
|
|
char chr=str[index];
|
|
if (state==1) { // Minus or first digit
|
|
if (chr=='-') {
|
|
state=2;
|
|
} else if (chr=='0') {
|
|
state=3;
|
|
} else if (chr>='1' && chr<='9') {
|
|
state=4;
|
|
} else {
|
|
return ("Invalid first character "+InternalTools.logSafeCharacter(chr)+", should be digit or minus sign");
|
|
}
|
|
} else if (state==2) { // First digit after minus
|
|
if (chr=='0') {
|
|
state=3;
|
|
} else if (chr>='1' && chr<='9') {
|
|
state=4;
|
|
} else {
|
|
return ("Expecting at least one digit after minus sign, got "+InternalTools.logSafeCharacter(chr)+" instead");
|
|
}
|
|
} else if (state==3) { // Number started with (minus) zero, only decimal point or E can follow
|
|
if (chr=='.') {
|
|
state=5;
|
|
} else if (chr=='e' || chr=='E') {
|
|
state=7;
|
|
} else {
|
|
return ("Only decimal point or E/e can follow number starting with 0 or -0, got "+InternalTools.logSafeCharacter(chr)+" instead");
|
|
}
|
|
} else if (state==4) { // Digits before decimal point or E
|
|
if (chr=='.') {
|
|
state=5;
|
|
} else if (chr=='e' || chr=='E') {
|
|
state=7;
|
|
} else if (chr<'0' || chr>'9') {
|
|
return ("Invalid character "+InternalTools.logSafeCharacter(chr)+" (before possible decimal point or E/e)");
|
|
}
|
|
} else if (state==5) { // Need at least one digit after decimal point
|
|
if (chr>='0' && chr<='9') {
|
|
state=6;
|
|
} else {
|
|
return ("Need at least one digit after decimal point, got "+InternalTools.logSafeCharacter(chr)+" instead");
|
|
}
|
|
} else if (state==6) { // Following digits after decimal point
|
|
if (chr=='e' || chr=='E') {
|
|
state=7;
|
|
} else if (chr<'0' || chr>'9') {
|
|
return ("Invalid character "+InternalTools.logSafeCharacter(chr)+" after decimal point");
|
|
}
|
|
} else if (state==7) { // Plus, minus or digit after E
|
|
if (chr=='+' || chr=='-') {
|
|
state=8;
|
|
} else if (chr>='0' && chr<='9') {
|
|
state=9;
|
|
} else {
|
|
return ("Expecting digit or plus/minus sign after E/e, got "+InternalTools.logSafeCharacter(chr)+" instead");
|
|
}
|
|
} else if (state==8) { // Need at least one digit after E plus/minus,
|
|
if (chr>='0' && chr<='9') {
|
|
state=9;
|
|
} else {
|
|
return ("Expecting digit after plus/minus sign after E/e, got "+InternalTools.logSafeCharacter(chr)+" instead");
|
|
}
|
|
} else if (state==9) { // Following digits after E
|
|
if (chr<'0' || chr>'9') {
|
|
return ("Invalid character "+InternalTools.logSafeCharacter(chr)+" in digits after E/e");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state==1) {
|
|
return "String is empty";
|
|
}
|
|
if (state==2) {
|
|
return "String contains only minus sign";
|
|
}
|
|
if (state==5) {
|
|
return "No digits after decimal point";
|
|
}
|
|
if (state==7) {
|
|
return "Need at least one digit after E/e";
|
|
}
|
|
if (state==8) {
|
|
return "Need at least one digit after plus/minus sign after E/e";
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
private string safeFloatAsString(float value) {
|
|
// Basic change that doesn't add too many decimals
|
|
string basicStr=value.ToString(CultureInfo.InvariantCulture);
|
|
basicStr=addDecimalPoint(basicStr);
|
|
try {
|
|
float reverse=float.Parse(basicStr,CultureInfo.InvariantCulture);
|
|
if (reverse.Equals(value)) { // May not be true because rounding
|
|
return basicStr;
|
|
}
|
|
}
|
|
catch (OverflowException) {
|
|
// This happens because rounding if value is close min/max
|
|
}
|
|
// Try out with more decimals
|
|
string rStr=value.ToString("R",CultureInfo.InvariantCulture);
|
|
rStr=addDecimalPoint(rStr);
|
|
try {
|
|
float reverse=float.Parse(rStr,CultureInfo.InvariantCulture);
|
|
if (reverse.Equals(value)) {
|
|
return rStr;
|
|
}
|
|
}
|
|
catch (Exception) {
|
|
// Intentionally empty, falling back
|
|
}
|
|
// If neither works, fall back to simplest
|
|
return basicStr;
|
|
}
|
|
|
|
private string safeDoubleAsString(double value) {
|
|
// Basic change that doesn't add too many decimals
|
|
string basicStr=value.ToString(CultureInfo.InvariantCulture);
|
|
basicStr=addDecimalPoint(basicStr);
|
|
try {
|
|
double reverse=double.Parse(basicStr,CultureInfo.InvariantCulture);
|
|
if (reverse.Equals(value)) {
|
|
return basicStr;
|
|
}
|
|
}
|
|
catch (OverflowException) {
|
|
// This happens because rounding if value is close min/max
|
|
}
|
|
// Try out with more decimals
|
|
string rStr=value.ToString("R",CultureInfo.InvariantCulture);
|
|
rStr=addDecimalPoint(rStr);
|
|
try {
|
|
double reverse=double.Parse(rStr,CultureInfo.InvariantCulture);
|
|
if (reverse.Equals(value)) {
|
|
return rStr;
|
|
}
|
|
}
|
|
catch (Exception) {
|
|
// Intentionally empty, falling back
|
|
}
|
|
// If neither works, fall back to simplest
|
|
return basicStr;
|
|
}
|
|
|
|
private static string addDecimalPoint(string str) {
|
|
if (str.IndexOf('E')==-1 && str.IndexOf('e')==-1 && str.IndexOf('.')==-1) {
|
|
str+=".0";
|
|
}
|
|
return str;
|
|
}
|
|
|
|
private static string safeLongAsString(long value) {
|
|
return value.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
internal override object zDeserialize(Type type, string toFieldName, DeserializeSettings deserializeSettings) {
|
|
|
|
// In case type is nullable type, for example "int?"
|
|
Type nullableType = Nullable.GetUnderlyingType(type);
|
|
if (nullableType != null) {
|
|
return this.zDeserialize(nullableType, toFieldName, deserializeSettings);
|
|
}
|
|
|
|
if (type.IsEnum) {
|
|
if (deserializeSettings.AllowDeserializeIntsToEnums) {
|
|
return this.AsInt();
|
|
}
|
|
throw (DeserializeException.forNonMatchingEnumType(toFieldName));
|
|
}
|
|
|
|
if (type==typeof(float)) {
|
|
return this.AsFloat();
|
|
}
|
|
if (type==typeof(double)) {
|
|
return this.AsDouble();
|
|
}
|
|
if (type==typeof(decimal)) {
|
|
return this.AsDecimal();
|
|
}
|
|
|
|
if (type==typeof(int)) {
|
|
return this.AsInt();
|
|
}
|
|
if (type==typeof(long)) {
|
|
return this.AsLong();
|
|
}
|
|
if (type==typeof(short)) {
|
|
return this.AsShort();
|
|
}
|
|
if (type==typeof(byte)) {
|
|
return this.AsByte();
|
|
}
|
|
|
|
if (type==typeof(uint)) {
|
|
return this.AsUInt();
|
|
}
|
|
if (type==typeof(ulong)) {
|
|
return this.AsULong();
|
|
}
|
|
if (type==typeof(ushort)) {
|
|
return this.AsUShort();
|
|
}
|
|
if (type==typeof(sbyte)) {
|
|
return this.AsSByte();
|
|
}
|
|
|
|
if (type==typeof(object)) {
|
|
if (deserializeSettings.AllowFieldsToBeObjects) {
|
|
return this.AsObject();
|
|
}
|
|
throw (DeserializeException.forNonMatchingTypeObject(this, toFieldName));
|
|
}
|
|
|
|
throw (DeserializeException.forNonMatchingType(this,type,toFieldName));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|