This is a C# implementation of a function to determine whether a string represents a valid JSON number. The function is called IsJsonNumber and takes a single parameter, input, which is the string to be tested.
The function first checks if the input string is null or empty, in which case it returns false.This implementation is designed to conform to the JSON specification as from json.org.
using System;
namespace Json
{
public static class JsonNumber
{
public static bool IsJsonNumber(string input)
{
if (string.IsNullOrEmpty(input))
{
return false;
}
int dotIndex = input.IndexOf('.');
int exponentIndex = input.IndexOfAny(new[] { 'e', 'E' });
return IsInteger(Integer(input, dotIndex, exponentIndex))
&& IsFraction(Fraction(input, dotIndex, exponentIndex))
&& IsExponent(Exponent(input, exponentIndex))
&& !input.EndsWith(".");
}
private static bool IsDigits(string input)
{
foreach (char c in input)
{
if (!char.IsDigit(c))
{
return false;
}
}
return input.Length > 0;
}
private static string Integer(string input, int dotIndex, int exponentIndex)
{
if (dotIndex < 0 && exponentIndex < 0)
{
return input;
}
int end = dotIndex < 0 ? exponentIndex : dotIndex;
return input[..end];
}
private static bool IsInteger(string input)
{
if (string.IsNullOrEmpty(input))
{
return false;
}
if (input[0] == '-')
{
input = input[1..];
}
if (input.Length == 1 && input[0] == '0')
{
return true;
}
if (input[0] == '0')
{
return false;
}
return IsDigits(input);
}
private static string Fraction(string input, int dotIndex, int exponentIndex)
{
if (dotIndex < 0)
{
return "";
}
int start = dotIndex + 1;
int end = exponentIndex < 0 ? input.Length : exponentIndex;
return input[start..end];
}
private static bool IsFraction(string input)
{
if (string.IsNullOrEmpty(input))
{
return true;
}
if (!IsDigits(input))
{
return false;
}
return (input[0] != '0') || input.TrimEnd('0').Length > 0;
}
private static string Exponent(string input, int exponentIndex)
{
if (exponentIndex < 0)
{
return "";
}
int start = exponentIndex + 1;
return input[start..];
}
private static bool IsExponent(string input)
{
if (string.IsNullOrEmpty(input))
{
return true;
}
int signIndex = input.IndexOfAny(new[] { '+', '-' });
if (signIndex == -1)
{
// No sign, just digits
return IsDigits(input);
}
else if (signIndex == input.Length - 1)
{
// Sign at the end, not valid
return false;
}
else
{
// Sign with digits
string digits = input[(signIndex + 1) ..];
return IsDigits(digits) && digits.Length > 0;
}
}
}
}
I have one test that is failing.
[Fact]
public void TheExponentIsAlwaysComplete()
{
Assert.False(IsJsonNumber("22e"));
Assert.False(IsJsonNumber("22e+"));
Assert.False(IsJsonNumber("23E-"));
}
And if I try to fix it it will make the basic tests (like following) to fail:
[Fact]
public void CanBeZero()
{
Assert.True(IsJsonNumber("0"));
}
[Fact]
public void CanHaveASingleDigit()
{
Assert.True(IsJsonNumber("7"));
}
[Fact]
public void CanHaveMultipleDigits()
{
Assert.True(IsJsonNumber("70"));
}