A bit stuck, (confusing myself really, I think).
I am wanting to convert a value from a JSON string from it String representation of hex to an int. I only need to get the value I will never need to write the other way.
For instance
{
"name" : "someName",
"id" : "b1"
}
I have a class created
public class Person
{
public string name;
[JsonConverter(typeof(myConverter))]
public int id;
}
and my converter (I am not sure I understand this part correctly)
public class myConverter : JsonConverter
{
//CanConvert Boiler Plate
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string sValue = existingValue.ToString().ToUpper();
int iValue = Convert.ToInt32(sValue);
return iValue;
}
I will of course add additional checks to validate data and what not, but I wanted to start with something to see how it worked.
So with the above example I would want my int to be 177
Any help, guidance would be appreciated.
Try following Command to Convert a Hex-Number to an int
var hex = "b1"
int intValue = int.Parse(hex, System.Globalization.NumberStyles.HexNumber);
Are you using Newtonsoft.Json? I notice your code wouldn't work on .NET System.Text.Json since:
There is no public non-generic version of JsonConverter.
It wouldn't work on id because it's not a property.
Here's the working code for System.Text.Json:
public class HexConverter : JsonConverter<int>
{
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var value = reader.GetString();
return Convert.ToInt32(value, 16);
}
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
The class has to contain properties to be deserialized:
public class Person
{
public string name { get; set; }
[JsonConverter(typeof(HexConverter))]
public int id { get; set; }
}
Usage:
const string json = #"{
""name"" : ""someName"",
""id"" : ""b1""
}";
var person = JsonSerializer.Deserialize<Person>(json, new JsonSerializerOptions());
Console.WriteLine(person!.id);
// Result: 177
This case Convert.ToInt32 works for me, but you can also use int.Parse as suggested in the other answer.
Related
I am using this to deserialize my Json response from an Api.
var apiResponse = await GetAsync<MyResponseModel>(request);
In my response model there is property that is an int, but the api for some reason formats it as a float.
So it looks like this:
{
"Quantity": 6.000
}
Now I parse it with this trick:
[JsonProperty("Quantity")]
private float QuantityFloat {
set => Quantity = IsInt(value) ? (int) value: throw new ArgumentException("Tried to parse number to Quantity that is not an int.");
}
public int Quantity { get; set; }
private static bool IsInt(float value)
{
var x = (int) value;
var temp2 = value - x;
return temp2 <= 0;
}
My linter now complains: "Properties with only setters are confusing and counterintuitive. Instead, a property getter should be added if possible, or the property should be replaced with a setter method."
So I was asking myself if there is a better more elegant way of doing this.
I would suggest creating a JsonConverter<int> that can handle both integer and decimal-formatted values:
(This solution is for System.Text.Json)
public class IntConverter : JsonConverter<int>
{
public override int Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TryGetInt32(out var intVal))
{
return intVal;
}
// customize this part as necessary to satisfactorily deserialize
// the float value as int, or throw exception, etc.
float floatValue = reader.GetSingle();
return (int)floatValue;
}
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Then you can decorate your property with [JsonConverter(typeof(IntConverter))] which will cause the converter to be used during serialization:
public class Model
{
[JsonConverter(typeof(IntConverter))]
public int Quantity { get; set; }
}
Handling it like this takes away the need for any extra properties, thus simplifying your solution. This is especially true if you have to handle this kind of scenario in multiple places.
Try it online
JSON.NET Solution:
public class IntConverter : JsonConverter<int>
{
public override int ReadJson(JsonReader reader, Type objectType, int existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Float)
{
// customize this part as necessary to satisfactorily deserialize
// the float value as int, or throw exception, etc.
double value = (double)reader.Value;
return (int)value;
}
long intVal = (long)reader.Value;
return (int)intVal;
}
public override void WriteJson(JsonWriter writer, int value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
with the following model:
public class Model
{
[JsonConverter(typeof(IntConverter))]
public int Quantity { get; set; }
}
Try it online
I'm using System.Text.Json in my ASP.NET Core C# solution and by default, I have the JSON string enum converter enabled. However, on a handful of fields, I'd like to return my enums as the underlying byte/int type without having to
set the type to a byte/int (I want to retain the fact it's an enum) or;
removing the JSON string enum converter from everything.
Is there a way of using [JsonConverter(type)] on a specific field to force the enum to output as the byte/int instead of the string?
You can use a custom converter, for example:
public class JsonIntEnumConverter<T> : JsonConverter<T> where T : Enum
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
Enum test = (Enum)Enum.Parse(typeof(T), value.ToString());
writer.WriteNumberValue(Convert.ToInt32(test));
}
}
Now in you class, any properties you want to keep as int, just add the JsonConverter attribute:
[JsonConverter(typeof(JsonIntEnumConverter<MyEnum>))]
public MyEnum SomeValue { get; set; }
I would not dismiss the int solution.
It's very simple and very clear to consumers - no surprise that enum comes as number.
public enum Number { Zero, One, Two, Three };
public class X
{
[JsonIgnore]
public Number N { get; set; }
public int NVal => (int)N;
public Number N2 { get; set; }
}
Then with
var o = new X { N = Number.One, N2 = Number.Two };
var serializeOptions = new JsonSerializerOptions
{
WriteIndented = true,
Converters = { new JsonStringEnumConverter() }
};
Console.WriteLine(JsonSerializer.Serialize(o, serializeOptions));
the result is:
{
"NVal": 1,
"N2": "Two"
}
How can i set FloatParseHandling.Decimal for a custom JsonConverter?
we have a struct DecimalDbValue that internally only holds one decimal field that i want to be de/serialized for all its types.
It uses a magic number (decimal.MinValue) for indicating a "null" value. It was created before .net 2.0 having nullable value types!
This is a spripped down version of our struct::
[Serializable]
[JsonConverter(typeof(DecimalDbValueJsonConverter))]
public struct DecimalDbValue : ISerializable
{
private readonly Decimal _decValue;
public DecimalDbValue(
decimal init)
{
_decValue = init;
}
[JsonConstructor]
public DecimalDbValue(
decimal? init)
{
if (init.HasValue)
_decValue = init.Value;
else
_decValue = decimal.MinValue;
}
private DecimalDbValue(
SerializationInfo objSerializationInfo,
StreamingContext objStreamingContext)
{
_decValue = objSerializationInfo.GetDecimal("value");
}
public bool IsNotNull
{
get
{
return !IsNull;
}
}
public bool IsNull
{
get
{
return _decValue.Equals(Decimal.MinValue);
}
}
public Decimal Value
{
get
{
return _decValue;
}
}
public void GetObjectData(
SerializationInfo info,
StreamingContext context)
{
info.AddValue("value", _decValue);
}
}
I created a JsonConverter:
class DecimalDbValueJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(DecimalDbValue) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = reader.Value == null ? (decimal?)null : Convert.ToDecimal(reader.Value);
return new DecimalDbValue(value);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dbValue = (DecimalDbValue)value;
if (dbValue.IsNull)
writer.WriteNull();
else
writer.WriteValue(dbValue.Value);
}
}
and set attribute [JsonConverter(typeof(DecimalDbValueJsonConverter))] on the DecimalDbValue struct
I have added a test:
[Test]
public void TestMaxDecimalDbValue()
{
var s = new DecimalDbValue(decimal.MaxValue);
var json = JsonConvert.SerializeObject(s, Formatting.Indented);
var x = JsonConvert.DeserializeObject<DecimalDbValue>(json);
Assert.AreEqual(s, x);
}
but it throws:
System.OverflowException : Value was either too large or too small for a Decimal.
How can i set FloatParseHandling.Decimal for the JsonConverter? How to make it work for MaxValue as well? Is there any other way?
Actually i would like to have it serialize/deserialized exactly like an decimal? (nullable decimal)
Thanks
It seems to be impossible.
Therefor i removed the JsonConverter completely.
Set this attribute on the struct:
[JsonObject(MemberSerialization.OptIn)]
as well as this on the constructor:
[JsonConstructor]
public DecimalDbValue(
decimal? init) //IMPORTANT: if you change the name of init - rename the JsonProperty below too - you might break all existing json out there thou!
as well as this on the get property:
[JsonProperty("init")]
public Decimal Value
{
get
{
return _decValue;
}
}
Unfortunately this bloats the json:
{
"init": 79228162514264337593543950335.0
}
In case it helps, you can set it for the whole JsonSerializer:
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
FloatParseHandling = FloatParseHandling.Decimal
};
_innerSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
You might also want to have a look on FloatFormatHandling to handle your nullables.
https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_FloatFormatHandling.htm
So I am using Json.net to deserialize json data returned by REST APIs to a server, the data structure it returns is:
{ "keyXXX" : [[1,2,3,...]...], "last": 12345 }
The problem is the "key" is also part of data, it is not a field named "key", so I cannot use a class/struct, I had to use a IDictionary<string, int[][]> for the first part, but then the "last" part will throw an exception, because it is a single integer rather than an array of arrays.
This is what I've tried:
var dec = JsonConvert.DeserializeObject<IDictionary<string, int[][]>>(data);
This throws json exception:
Newtonsoft.Json.JsonSerializationException: Error converting value 1501555920 to type 'System.Decimal[][]'. Path 'last'.
//define a class
public class DPInfo
{
public decimal[][] points { get; set; }
public long last { get; set; }
}
var dec = JsonConvert.DeserializeObject<DPInfo>(data);
This will not work because the field name of the array is dynamic, so points will contains nothing after this.
Any way I can fix this?
You will need a custom JsonConverter to solve this. Here is one that should work:
public class DPInfoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DPInfo);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
JProperty points = obj.Properties().FirstOrDefault(p => p.Name != "last");
DPInfo info = new DPInfo
{
key = points.Name, // remove this line if you don't need the key
points = points.Value.ToObject<decimal[][]>(),
last = (long)obj["last"]
};
return info;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use it, just add a [JsonConverter] attribute to your class:
[JsonConverter(typeof(DPInfoConverter))]
public class DPInfo
{
public string key { get; set; } // remove this line if you don't need the key
public decimal[][] points { get; set; }
public long last { get; set; }
}
Then, deserialize as usual, and it should work:
string json = #"
{
""keyXXX"": [[1, 2, 3]],
""last"": 12345
}";
DPInfo info = JsonConvert.DeserializeObject<DPInfo>(json);
Fiddle: https://dotnetfiddle.net/7S6STp
You could use JObject.Parse to parse your string into a dynamic variable. It would at least ensure that the conversion from string to JSON succeeds, but then it would be up to you to validate that there is a value in each property before accessing it.
In your case the statement would look like this:
dynamic data = JObject.Parse("{ 'keyXXX' : [[1,2,3,...]...], 'last': 12345 }");
I am receiving json from a server which I am converting to an object using Json.Net. For one member I am using the StringEnumConverter which works really perfect.
However all of a sudden the server decided to use strings which also can start with a number which results in a JsonSerilizationException - obviously because enums cannot start with a number in .Net.
Now I am trying to find a solution to handle that.My first approach was to add a "_" when Reading the Json (so my enums in the code would have a starting _ when they are followed by a number) and when writing the json I would delete the starting _ (if a number is following). To achieve this I copied the StringEnumConverter into my namespace and tried to change the according part in the WriteJson and ReadJson methods. However I cannot use the StringEnumConverter since there are other dependencies I cannot access in my own namespace.
Is there any elegant solution to this problem?
You can create a JsonConverter and trim the digits from the front
public class Json_34159840
{
public static string JsonStr = #"{""enum"":""1Value"",""name"":""James"",""enum2"":""1""}";
public static void ParseJson()
{
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new EnumConverter() }
};
// Later on...
var result = JsonConvert.DeserializeObject<JsonClass>(JsonStr);
Console.WriteLine(result.Enum);
Console.WriteLine(result.Enum2);
Console.WriteLine(result.Name);
}
}
public class EnumConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var str = value.ToString();
if (Regex.IsMatch(str, #"^_"))
{
writer.WriteValue(str.Substring(1));
}
else
{
writer.WriteValue(str);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = reader.Value.ToString();
if (Regex.IsMatch(value, #"^\d+$"))
{
return Enum.Parse(objectType, value);
}
if (Regex.IsMatch(value, #"^\d+"))
{
value = "_" + value;
}
return Enum.Parse(objectType, value);
}
public override bool CanConvert(Type objectType)
{
//You might want to do a more specific check like
//return objectType == typeof(JsonEnum);
return objectType.IsEnum;
}
}
public enum JsonEnum
{
_0Default,
_1Value
}
public class JsonClass
{
public string Name { get; set; }
public JsonEnum Enum { get; set; }
public JsonEnum Enum2 { get; set; }
}
Hope this helps.
EDIT: Added support for integers :D
A simple strategy is to deserialize the value into a string property and then convert into your own data type (enum or otherwise) via an accessor method or a secondary getter-only property.