Why does JsonConvert deserialize object fail with int but not long - c#

When testing av web API created in .Net 6.0, we found that when a user of the API sent decimal number on a int you got a 400 error stating that it fail to parse the json because of the decimal on the int value. But doing the same on a long value worked fine it just removed the decimal numbers.
So to test if this (guessing that MS uses Newonsoft.Json), I made a little cmd test app to test the scenario. And the same thing happens there long pareses loosing it decimals, and int fails.
So is this a bug in the parser or by design?
[Edit] Should it not also fail on long?
using Newtonsoft.Json;
var data = JsonConvert.DeserializeObject<SomData>(#"{""aInt"":1, ""ALong"":2.2}");
Console.WriteLine(data.ALong); // output 2
var data2 = JsonConvert.DeserializeObject<SomData>(#"{""aInt"":1.2, ""ALong"":2}"); // exception
Console.WriteLine(data2.AInt);
internal class SomData
{
public int AInt { get; set; }
public long ALong { get; set; }
}

The answer is by design. we can see this discussion Error converting float value to integer in Web API #654. The author's answer was below.
Later versions of Json.NET throw an error when deserializing a floating-point value onto an integer property.
I would always use decimal when you are using floating-point numbers.
internal class SomData
{
public decimal AInt { get; set; }
public decimal ALong { get; set; }
}
EDIT
I had seen the source code of Json.Net
The Int value will will go into else part as below code from JsonTextReader in ReadType.ReadAsInt32
which might as author say by design.
ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out int value);
if (parseResult == ParseResult.Success)
{
numberValue = value;
}
else if (parseResult == ParseResult.Overflow)
{
throw ThrowReaderError("JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}
else
{
throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}
but let's see ReadType.ReadAsInt64 that else part is a lot of different between ReadAsInt32.
At First, it would get to else let value(object type) to store as float value as below code.
ParseResult parseResult = ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out long value);
if (parseResult == ParseResult.Success)
{
numberValue = value;
numberType = JsonToken.Integer;
}
else if (parseResult == ParseResult.Overflow)
{
#if HAVE_BIG_INTEGER
string number = _stringReference.ToString();
if (number.Length > MaximumJavascriptIntegerCharacterLength)
{
throw ThrowReaderError("JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}
numberValue = BigIntegerParse(number, CultureInfo.InvariantCulture);
numberType = JsonToken.Integer;
#else
throw ThrowReaderError("JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
#endif
}
else
{
if (_floatParseHandling == FloatParseHandling.Decimal)
{
parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal d);
if (parseResult == ParseResult.Success)
{
numberValue = d;
}
else
{
throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}
}
else
{
string number = _stringReference.ToString();
if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double d))
{
numberValue = d;
}
else
{
throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
}
}
numberType = JsonToken.Float;
}
Then the number will be convert to Int64 by JsonSerializerInternalReader.EnsureType
// this won't work when converting to a custom IConvertible
return Convert.ChangeType(value, contract.NonNullableUnderlyingType, culture);
so we can get long will not get execption but int will, not sure why ReadAsInt64 allow store as float but int was not.

Related

When does a double become NaN?

I am trying to understand which circumstances can cause a double to become NaN.
For instance, 0/0 is nan. This is also stated in the official Documentation.
https://learn.microsoft.com/en-us/dotnet/api/system.double.nan?view=net-6.0
However, my debug code indicates that the division is not by 0:
public class StandardDeviation
{
public StandardDeviation()
{
Clear();
}
private double M { get; set; }
private double S { get; set; }
private uint Iteration { get; set; }
public double Value
{
get
{
return Math.Sqrt(S / (Iteration - 2));
}
}
public void AddValue(double value)
{
if (double.IsNaN(value))
{
throw new ArgumentException("value IS nan!");
}
double tmpM = M;
M += (value - tmpM) / Iteration;
S += (value - tmpM) * (value - M);
Iteration++;
if (double.IsNaN(M))
{
Console.WriteLine($"\nNAN EXCEPTION!!! divide by: {Iteration}");
Console.WriteLine($"\nNAN EXCEPTION!!! tmpM: {tmpM}");
throw new ArgumentException("m IS nan!");
}
}
public void Clear()
{
M = 0.0;
S = 0.0;
Iteration = 1;
}
}
console message:
NAN EXCEPTION!!! divide by: 3
NAN EXCEPTION!!! tmpM: ∞
The issue primairly seems to be prevalent in release configuration, which makes it hard to debug.
Whenever the exception is thrown, Iteration = 3.

Simple Unit Testing - Parse Invalid Input To Throw Error C# Visual Studio

I have a very basic method that divides two double values.
For unit testing I want to include a invalid input (string) to throw error message or exception. What is most simple way to parse the value or fail the test (expected)?
CalculatorClass.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Calculator
{
public class CalculatorClass
{
//METHODS
public double Divide(double num1, double num2)
{
double result = num1 / num2;
return result;
}
}
}
UnitTest1.cs
using System;
using Calculator; //ADD REFERENCE
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CalcMethodTest
{
//AreEqual
//AreNotEqual
//AreNotSame
//AreSame
//Equals
//Fail
//Inconclusive
//IsFalse
//IsInstanceOfType
//IsNotNull
//IsNull
//IsTrue
//ReplaceNullChars
[TestClass]
public class UnitTest1
{
[TestMethod]
public void _1_3_Test_Divide_Input_Seven_2_Output_Error()
{
//ARRANGE
CalculatorClass calcObj = new CalculatorClass();
string expectedOutput = "Error - Invalid Input";
//ACT
//----HERE WRONG DATA TYPE FOR TESTING----
double result = calcObj.Divide("Seven", 2);
//ASSERT
Assert.AreEqual(expectedOutput, result);
}
}
}
Since your Divide method takes input of double,double the string wrong data type you use cannot be used as input.
In order to allow the input to be string or number, I suggest you to change the argument type to a base class common to both (say, object) and then expand the Divide by trying to parse the data - returning false if the process cannot be finished (or if the exception occur, up to you), similar to TryParse method provided by .Net. You can also extend the out variable to include the error string if you find it suitable.
Also, more appropriate name would be TryDivide than Divide:
namespace Calculator {
public class CalculatorClass {
//METHODS
public bool TryDivide(object num1, object num2, out double doubleVal, out string errorString) {
doubleVal = 0;
errorString = string.Empty;
try {
if (num1 == null || num2 == null) {
errorString = "number(s) cannot be null";
return false;
}
double num = 0, den = 0;
bool parseResult;
if (num1 is double)
num = (double)num1;
else {
parseResult = double.TryParse(num1.ToString(), out num);
if (!parseResult) {
errorString = "numerator cannot be parsed as double";
return false;
}
}
if (num2 is double)
den = (double)num2;
else {
parseResult = double.TryParse(num2.ToString(), out den);
if (!parseResult) {
errorString = "denominator cannot be parsed as double";
return false;
}
}
doubleVal = num / den;
return true;
} catch (Exception ex) {
errorString = ex.ToString();
return false; //may also be changed to throw
}
}
}
}
Just then you will be able call your TryDivide with string input:
double doubleResult;
string errorString;
bool result = calcObj.TryDivide("Seven", 2, out doubleResult, out errorString);
if (!result){ //something is wrong
Console.WriteLine(errorString);
}
You are not able to pass a string where a double parameter is expected. If you absolutely need to be able to pass a string parameter to this method, you should not expect it to fail based on it being an invalid type - only if the conversion to a double failed. In that case, I'd attempt some rudimentary parsing of a string to a double (however in that case you'd probably only want to parse "7", not "seven" - up to you though).
What you have written will never be able to get tested, purely because it will never compile using C#.
If all you want is to test exception handling in unit tests and how you can fail tests by passing wrong arguments, look at this example.
public class Sum
{
//Returns the sum of 2 positive odd integers
//If either of arguments is even, return -1
//If either of arguments is negative, throw exception
public int PositiveSumOddOnly(int a, int b)
{
if(a < 0 || b < 0)
throw new InvalidArgumentException("One or more of your arguments is negative");
if(a%2 == 0 || b%2 == 0)
return -1;
return a + b;
}
}
[TestClass]
public class Sum_Test
{
[TestMethod]
public int PositiveSumOddOnly_ShouldThrowInvalidArgumentExecption(int a, int b)
{
Sum s = new Sum();
try
{
int r = s.PositivesumOddOnly(1,-1);
}
catch(InvalidArgumentException e)
{
Assert.AreEqual("One or more of your arguments is negative", e.Message);
}
}
[TestMethod]
public int PositiveSumOddOnly_ShouldReturnNegativeOne(int a, int b)
{
Sum s = new Sum();
int r = s.PositiveSumOddOnly(1,2);
Assert.AreEqual(r,-1);
}
[TestMethod]
public int PositiveSumOddOnly_ShouldReturnSumOfAandB(int a, int b)
{
Sum s = new Sum();
int r = s.PositiveSumOddOnly(1,1);
Assert.AreEqual(r,2);
}
}
This seems to work:
[TestMethod]
public void _1_3_Test_Divide_Input_Seven_2_Output_Error()
{
//ARRANGE
CalculatorClass calcObj = new CalculatorClass();
string expectedOutput = "Error - Invalid Input";
//ACT
//----CHANGED TO STRING. THEN CAST BACK TO DOUBLE INSIDE METHOD----
string result = calcObj.Divide("Seven", "2");
//ASSERT
Assert.AreEqual(expectedOutput, result);
}
Method:
public string Divide(string num1, string num2)
{
string resultMsg = "";
try
{
double num1Dbl = double.Parse(num1);
double num2Dbl = double.Parse(num2);
double result = num1Dbl / num2Dbl;
resultMsg = result.ToString();
return resultMsg;
}
catch (FormatException error)
{
resultMsg = "Error - Invalid Input";
return resultMsg;
}
}

StackOverflow Exception from get and set

I have the following code:
namespace QuantStrats
{
class Program
{
static void Main(string[] args)
{
string FilePath = "C:\\Users\\files\\DJ.csv";
StreamReader streamReader = new StreamReader(FilePath);
string line;
List<Data> Data = new List<Data>();
while ((line = streamReader.ReadLine()) != null)
{
Data Tick = new Data();
string [] values = line.Split(',');
Tick.SetFields(values[1], values[2]);
Data.Add(Tick);
}
for (int ii = 0; ii < Data.Count; ii++)
{
Data TickDataValues = new Data();
TickDataValues = Data[ii];
Console.Write("TIME :" + TickDataValues.time + " Price : " + TickDataValues.price + Environment.NewLine);
}
Console.ReadLine();
}
}
class Data
{
public DateTime time
{
get { return this.time; }
set
{
this.time = value;
}
}
public double price
{
get { return this.price; }
set
{
this.price = value;
}
}
public void SetFields(string dateTimeValue, string PriceValue)
{
try
{
this.time = Convert.ToDateTime(dateTimeValue);
}
catch
{
Console.WriteLine("DateTimeFailed " + dateTimeValue + Environment.NewLine);
}
try
{
this.price = Convert.ToDouble(PriceValue);
}
catch
{
Console.WriteLine("PriceFailed " + PriceValue + Environment.NewLine);
}
}
}
}
But I get a stack overflow exception.
I know it is because I am not doing my get and sets correctly and am entering an infinite loop, but I cannot see why exactly this is happening?
public DateTime time
{
get { return this.time; }
set
{
this.time = value;
}
}
you aren't using backing fields, but setting the property itself from within the property setter.
You can fix this by using 1) an auto property
public DateTime Time { get; set; }
or 2) a backing field
private DateTime _time;
public Datetime Time
{
get { return _time; }
set { _time = value; }
}
they both equate to the same code.
For an explanation, when you get time in your code:
get { return this.time; }
it has to retrieve the value of time to return. It does that by calling the get on time, which has to get retrieve the value of time, etc.
I cannot see why exactly this is happening?
public double price
{
get { return this.price; }
set
{
this.price = value;
}
}
When you "get" price, the getter for price is called, which calls the getter for price, which calls the getter for price, which...
Just use auto-implement properties if you don't want to mess with a backing field:
public DateTime Time {get; set;}
public double Price {get; set;}
Some other observations:
The standard convention for property names is to start them with a capital letter, which is why I changed your properties to Time and Price in my examples.
You may want to consider using decimal for a property like Price if you do any floating-point math, since double has some slight imprecision when representing decimal numbers like 1.1. decimal will store the number exacly without any loss of precision.
Just writing to the console in a catch block seems incorrect. You are basically ignoring the error (from a logic flow sense). Rather than accepting strings in the class and parsing them, I would do the validation in the calling code and making sure the inputs are valid before passing them to the class.
Properties getters and setters are really just getXXX and setXXX methods (that's how they are compiled). Because you set the property from the property itself, it is if you were recurring endlessly on a method.
public DateTime time()
{
return time();
}
As stated by other answers, you can use backing fields or auto-implemented properties.

A custom datetime struct

I am trying to develop a clock application with images for each digits from 0-9. Wrote a struct that gives me each digits every now and then. Following is the struct.
public struct TimeStruct
{
public DateTime dt
{
get
{
return DateTime.Now;
}
}
public int s
{
get
{
return dt.Second;
}
}
public int s2
{
get
{
return s % 10;
}
}
public int s1
{
get
{
return s / 10;
}
}
public int m
{
get
{
return dt.Minute;
}
}
public int m2
{
get
{
return m % 10;
}
}
public int m1
{
get
{
return m / 10;
}
}
public int h
{
get
{
return dt.Hour;
}
}
public int h2
{
get
{
return h % 10;
}
}
public int h1
{
get
{
return h / 10;
}
}
public int d
{
get
{
return (int)dt.DayOfWeek;
}
}
}
Please guide me to modify this struct so that the prop s2 should be set only when s1 becomes 0. And the same with minutes.
Technology Used : Silverlight
Platform : Windows Phone 7
Was that a bad idea to use struct?
What do you mean by "prop s2 should be set only when s1 becomes 0" - what do you want it to do when s1 isn't 0? Are you perhaps looking for nullable value types, where s1 would return the null value in some cases?
I have to say, I think this is a pretty confusing type. It has no real state - it's effectively just a bunch of static properties. Any reason for not implementing it as a bunch of static properties, e.g. in a CurrentDateTime class? Or just use DateTime.Now? Note that if you ask your struct for a bunch of values, one at a time, it may very well give you inconsistent results as time will pass. For example, suppose the time is 1:59:59 and you call s, then m, then h - you may end up getting 59, 59, 2 as the current time rolls over from 1:59:59 to 2:00:00 between the last two calls. If you take the value of DateTime.Now just once and ask it for all its properties, you'll get a consistent view.
Why re-invent the wheel ? Use DateTime and TimeSpan.

XPath vs DeSerialization: which one is better in performance for read operations

I'm passing small (2-10 KB)XML documents as input to a WCF service. now I've two option to read data values from incoming XML
Deserialize to a strongly typed object and use object properties to access values
use XPath to access values
which approach is faster? some statistics to support your answer would be great.
I would deserialize it.
If you use xpath, you will deserialize (or "load") it to XmlDocument or something anyway. So both solutions use time deserializing. After this is done, xpath will be slower because of the time spent parsing that string, resolving names, executing functions and so on. Also, if you go with xpath, you get no type safety. Your compiler can't check the xpath syntax for you.
If you use XmlSerializer and classes, you get static typing. Really fast access to you data, and if you want to query them with xpath, there are still ways to do that.
Also, I would like to say that your code would probably be easier to understand with classes.
The only drawback is that the xml has to conform to the same schema all the time, but that might not be a real problem in your case.
I hope that you forgive the absence of statistics, I think the arguments are strong enough without examples. If you want an ultimate answer, try both and keep a stopwatch ready.
There's a third option of sticking with XML, but query with whatever XML API you're using - e.g. LINQ to XML makes queries relatively straightforward in code.
Have you already parsed the text into an XML document?
Are you convinced that this is actually a significant performance bottleneck in your code? (e.g. if you're then talking to a database, then don't worry about this to start with - just get it to work in the simplest way first)
Are the queries always the same, or are they dynamic in some way?
Do you have a test rig with realistic messages and queries? If not, you need one in order to evaluate any answers given here with your data. If you do, I would expect it to be reasonably easy to try it yourself :)
Here are 4 cases, all times in ticks and placing:
XmlSerializer (Slowest 4th)
Implementing IXmlSerializable (3rd)
Hand Rolled (Custom) (1st)
XElement (2nd)
Sample object was read 1000 times.
Should you care? For majority of cases, use the default serializers that are built into .net. There is no need to deviate and that will produce the minimal amount of code. Those should be more than sufficient, offer type safety and free yourself to do more meaningful things with your time. In some cases, XElement may be useful if you wish to cherry pick certain data elements off of a large XML structure, but even then one should put those elements into a strongly typed DTO. But bear in mind, all methods are very fast. I've personally serialized an extremely broad and deep object model (well over 400 clases) in just a few milliseconds. For smaller and trivial objects, it will be sub ms response times. XMLSerializer warm up is slower than the others, but one can mitigate with SGEN or doing some initialization on startup.
Details and Code...
Xml Serializer
[Serializable]
public class FoobarXml
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsContent { get; set; }
[XmlElement(DataType = "date")]
public DateTime BirthDay { get; set; }
}
First Time: 2448965
1000 Read Average: 245
IXmlSerializable
public class FoobarIXml : IXmlSerializable
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsContent { get; set; }
public DateTime BirthDay { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
var isEmptyElement = reader.IsEmptyElement;
reader.ReadStartElement();
if (!isEmptyElement)
{
Name = reader.ReadElementString("Name");
int intResult;
var success = int.TryParse(reader.ReadElementString("Age"), out intResult);
if (success)
{
Age = intResult;
}
bool boolResult;
success = bool.TryParse(reader.ReadElementString("IsContent"), out boolResult);
if (success)
{
IsContent = boolResult;
}
DateTime dateTimeResult;
success = DateTime.TryParseExact(reader.ReadElementString("BirthDay"), "yyyy-MM-dd", null,
DateTimeStyles.None, out dateTimeResult);
if (success)
{
BirthDay = dateTimeResult;
}
reader.ReadEndElement(); //Must Do
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("Name", Name);
writer.WriteElementString("Age", Age.ToString());
writer.WriteElementString("IsContent", IsContent.ToString());
writer.WriteElementString("BirthDay", BirthDay.ToString("yyyy-MM-dd"));
}
}
}
First Time: 2051813
1000 Read Average: 208
Hand Rolled
public class FoobarHandRolled
{
public FoobarHandRolled(string name, int age, bool isContent, DateTime birthDay)
{
Name = name;
Age = age;
IsContent = isContent;
BirthDay = birthDay;
}
public FoobarHandRolled(string xml)
{
if (string.IsNullOrWhiteSpace(xml))
{
return;
}
SetName(xml);
SetAge(xml);
SetIsContent(xml);
SetBirthday(xml);
}
public string Name { get; set; }
public int Age { get; set; }
public bool IsContent { get; set; }
public DateTime BirthDay { get; set; }
/// <summary>
/// Takes this object and creates an XML representation.
/// </summary>
/// <returns>An XML string that represents this object.</returns>
public override string ToString()
{
var builder = new StringBuilder();
builder.Append("<FoobarHandRolled>");
if (!string.IsNullOrWhiteSpace(Name))
{
builder.Append("<Name>" + Name + "</Name>");
}
builder.Append("<Age>" + Age + "</Age>");
builder.Append("<IsContent>" + IsContent + "</IsContent>");
builder.Append("<BirthDay>" + BirthDay.ToString("yyyy-MM-dd") + "</BirthDay>");
builder.Append("</FoobarHandRolled>");
return builder.ToString();
}
private void SetName(string xml)
{
Name = GetSubString(xml, "<Name>", "</Name>");
}
private void SetAge(string xml)
{
var ageString = GetSubString(xml, "<Age>", "</Age>");
int result;
var success = int.TryParse(ageString, out result);
if (success)
{
Age = result;
}
}
private void SetIsContent(string xml)
{
var isContentString = GetSubString(xml, "<IsContent>", "</IsContent>");
bool result;
var success = bool.TryParse(isContentString, out result);
if (success)
{
IsContent = result;
}
}
private void SetBirthday(string xml)
{
var dateString = GetSubString(xml, "<BirthDay>", "</BirthDay>");
DateTime result;
var success = DateTime.TryParseExact(dateString, "yyyy-MM-dd", null, DateTimeStyles.None, out result);
if (success)
{
BirthDay = result;
}
}
private string GetSubString(string xml, string startTag, string endTag)
{
var startIndex = xml.IndexOf(startTag, StringComparison.Ordinal);
if (startIndex < 0)
{
return null;
}
startIndex = startIndex + startTag.Length;
var endIndex = xml.IndexOf(endTag, StringComparison.Ordinal);
if (endIndex < 0)
{
return null;
}
return xml.Substring(startIndex, endIndex - startIndex);
}
}
First Time: 161105
1000 Read Average: 29
XElement
var xDoc = XElement.Parse(xml);
var nameElement = xDoc.Element("Name");
var ageElement = xDoc.Element("Age");
var isContentElement = xDoc.Element("IsContent");
var birthDayElement = xDoc.Element("BirthDay");
string name = null;
if (nameElement != null)
{
name = nameElement.Value;
}
var age = 0;
if (ageElement != null)
{
age = int.Parse(ageElement.Value);
}
var isContent = false;
if (isContentElement != null)
{
isContent = bool.Parse(isContentElement.Value);
}
var birthDay = new DateTime();
if (birthDayElement != null)
{
birthDay = DateTime.ParseExact(birthDayElement.Value, "yyyy-MM-dd", CultureInfo.InvariantCulture);
}
First Time: 247024
1000 Read Average: 113

Categories