ServiceStack.Text deserialize string into single object null reference - c#

I have the following code. With JSON.NET it works fine where I can deserialize the string into the CustomMaker object. With ServiceStack.Text I get null. I've tried doing { get; set; } and removing and adding the constructor.
With JSON.NET it simply worked like JsonConvert.DeserializeObject(xString);
Any idea why this does not work with ServiceStack.Text?
static void Main(string[] args) {
string xString = "{\"error\":\"Invalid token 1 #556264\"}";
Console.WriteLine(xString);
CustomMaker xSLBM = TypeSerializer.DeserializeFromString<CustomMaker>(xString);
Console.WriteLine(string.IsNullOrWhiteSpace(xSLBM.error) ? "error is null" : xSLBM.error);
Console.ReadLine();
}
[Serializable]
public class CustomMaker {
public int UserID;
public String error;
public CustomMaker() { }
}
edit: This code also produces null:
static void Main(string[] args) {
JsConfig.IncludePublicFields = true;
string xString = "{\"error\":\"Invalid token 1 #556264\"}";
Console.WriteLine(xString);
CustomMaker xSLBM = TypeSerializer.DeserializeFromString<CustomMaker>(xString);
Console.WriteLine(string.IsNullOrWhiteSpace(xSLBM.error) ? "error is null" : xSLBM.error);
Console.ReadLine();
}
[Serializable]
public class CustomMaker {
public CustomMaker() { }
public int UserID { get; set; }
public String error { get; set; }
}

By Default ServiceStack only serializes public properties so you could refactor your DTO to include properties, e.g:
public class CustomMaker {
public int UserID { get; set; }
public String error { get; set; }
}
Or if you wanted to serialize public fields you can specify this with:
JsConfig.IncludePublicFields = true;
Also you need to use the JsonSerializer class, e.g:
CustomMaker xSLBM = JsonSerializer.DeserializeFromString<CustomMaker>(xString);
Or string.ToJson() or T.FromJson<T>(string) extension methods, e.g:
CustomMaker xSLBM = xString.FromJson<CustomMaker>();
The TypeSerializer class is only for serializing/deserializing the JSV Format, not JSON.

Related

System.Text.Json Deserialization using constructor

I am using System.Text.Json for deserialization.
I want to use the constructor in SerialNo(string serialNo) to build my object.
public class SerialNo
{
[JsonConstructor]
public SerialNo(string serialNo)
{
if (serialNo == null) throw new ArgumentNullException(nameof(serialNo));
if (string.IsNullOrWhiteSpace(serialNo)) throw new Exception("My exception text");
Value = serialNo.Trim('0');
}
public string Value { get; set; }
}
public class Item
{
public SerialNo SerialNo { get; set; }
public string AffiliationOrgCode { get; set; }
}
public class Root
{
public List<Item> Item { get; set; }
}
public class DeserializationTestsWithSystemTextJson
{
private const string JsonString = #"
{
""item"": [
{
""serialNo"": ""000000000002200878"",
""affiliationOrgCode"": ""OrgCode1""
},
{
""serialNo"": ""000000000002201675"",
""affiliationOrgCode"": ""OrgCode1""
}
]
}
";
[Fact]
public void Simple_Deserialization_With_SystemTextJson()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
var deserializedClass = JsonSerializer.Deserialize<Root>(JsonString, options);
Assert.NotNull(deserializedClass);
Assert.Equal("2201675", deserializedClass.Item[1].SerialNo.Value);
}
}
Fails with:
The JSON value could not be converted to JsonNetDeserialization.Test.WithSystemText.SerialNo. Path: $.item[0].serialNo | LineNumber: 3 | BytePositionInLine: 44.
Any ideas?
Since you are converting a string to a SerialNo, you need a custom converter. For example:
public class SerialNoConverter : JsonConverter<SerialNo>
{
public override SerialNo? Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
var serialNo = reader.GetString();
return new SerialNo(serialNo);
}
public override void Write(Utf8JsonWriter writer, SerialNo value,
JsonSerializerOptions options)
{
// Left for OP if required
throw new NotImplementedException();
}
}
And to use it you can either add an attribute to your class:
public class Item
{
[JsonConverter(typeof(SerialNoConverter))]
public SerialNo SerialNo { get; set; }
public string AffiliationOrgCode { get; set; }
}
Or add the converter to your serialiser options:
options.Converters.Add(new SerialNoConverter());
Here is a running example.
You are able to use a constructor to deserialize JSON using System.Text.Json, contrary to what the marked answer says. Even Microsoft documents it: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/immutability?pivots=dotnet-7-0
There are a some drawbacks though:
All class/struct fields/properties must match the JSON property name exactly
All json properties must not be nested in a deeper JSON object
To deserialize JSON with a constructor, you just need to use [JsonConstructor]. Here's an example of deserializing this way:
JSON:
{
"Fp": 2.199,
"Pi": 3.14159,
"Str": "Hello World"
}
The deserialized class:
public class Dto
{
public double Fp { get; set; }
public double Pi { get; set; }
public string Str { get; set; }
[JsonConstructor]
public Dto(double fp, double pi, string str)
{
Fp = fp;
Pi = pi;
Str = str;
}
public override string ToString()
{
return $"{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}";
}
}
Now to deserialize, you just include this expression: var dto = JsonSerializer.Deserialize<Dto>(Json); In the event you are trying to deserialize are custom type, you can include the [JsonConverter(...)] attribute over the custom type properties/fields in your deserialized class if the custom types do not already have a JsonConverter attribute for their class. Because the class properties use the exact name as in the Json, all other types are still deserialized properly and the custom type property will use the attributed custom converter. Here is an example of using a custom converter:
public class Dto
{
public double Fp { get; set; }
public double Pi { get; set; }
[JsonConverter(typeof(CustomStringConverter))] // This can be removed if the CustomString class already has a JsonConverter attribute
public CustomString Str { get; set; }
[JsonConstructor]
public Dto(double fp, double pi, CustomString str)
{
this.Fp = fp;
Pi = pi;
Str = str;
}
public override string ToString()
{
return $"{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}";
}
}

XML desirialization to appropriate type based on attributes present in XML C#

I am facing a challenge deserializing an XML to appropriate type. Request your help.
I have two xml files. oldXML.xml and newXML.xml below respectively
<?xml version="1.0"?>
<root>
<elementOne>101</elementOne>
<elementTwo>10</elementTwo>
</root>
And
<?xml version="1.0"?>
<root>
<elementOne>101</elementOne>
<elementTwo>10</elementTwo>
<elementThree>10</elementThree>
</root>
newXML.xml has an additional attribute "elementThree"
I have written 3 classes to desirialize the XMLs into
public abstract class ResponseBase
{
public abstract void PrintResult();
}
public class OldXML : ResponseBase
{
[XmlElement("elementOne")]
public string ElementOne { get; set; }
[XmlElement("elementTwo")]
public string ElementTwo { get; set; }
public override void PrintResult()
{
Console.WriteLine("Result is of type 'OldXML': {0}, {1}", ElementOne, ElementTwo);
}
}
public class NewXML : ResponseBase
{
[XmlElement("elementOne")]
public string ElementOne { get; set; }
[XmlElement("elementTwo")]
public string ElementTwo { get; set; }
[XmlElement("elementThree")]
public string ElementThree { get; set; }
public override void PrintResult()
{
Console.WriteLine("Result is of type 'NewXML': {0}, {1}, {2}", ElementOne, ElementTwo, ElementThree);
}
}
And I want to deserialize them as below
ResponseBase result1= MethodToDeserializeToBeWritten(File.ReadAllText("oldXML.json"))
ResponseBase result2= MethodToDeserializeToBeWritten(File.ReadAllText("newXML.json"))
result1.PrintResult()
result2.PrintResult()
When I Invoke PrintResult method, at the mercy of polymorphism in OOPS, child class implementation should be invoked (Not working, throws an error that abstract class cannot be instantiated). Please note that these XMLs are just examples and the code should work for any such XMLs.
Also, the XML is received from a client and hence we cannot change the XML.
The reason for doing this is, in future, we might get a new XML with new attribute say "elementFour". For that, we will be adding a new class and not touching the existing implementation.
Thanks in advance.
You need to create a generic MethodToDeserializeToBeWritten method.
public class XML
{
public static T Deserialize<T>(string input)
{
var serializer = new XmlSerializer(typeof(T));
using (TextReader textReader = new StringReader(input))
{
return (T)serializer.Deserialize(textReader);
}
}
}
public abstract class ResponseBase
{
public abstract void PrintResult();
}
[XmlRoot("root")]
public class OldXML : ResponseBase
{
[XmlElement("elementOne")]
public string ElementOne { get; set; }
[XmlElement("elementTwo")]
public string ElementTwo { get; set; }
public override void PrintResult()
{
Console.WriteLine("Result is of type 'OldXML': {0}, {1}", ElementOne, ElementTwo);
}
}
[XmlRoot("root")]
public class NewXML : ResponseBase
{
[XmlElement("elementOne")]
public string ElementOne { get; set; }
[XmlElement("elementTwo")]
public string ElementTwo { get; set; }
[XmlElement("elementThree")]
public string ElementThree { get; set; }
public override void PrintResult()
{
Console.WriteLine("Result is of type 'NewXML': {0}, {1}, {2}", ElementOne, ElementTwo, ElementThree);
}
}
class Program
{
static void Main(string[] args)
{
string rootFilePath = #"C:\test\";
ResponseBase result1 = MethodToDeserializeToBeWritten<OldXML>(File.ReadAllText($"{rootFilePath}oldXML.xml"));
ResponseBase result2 = MethodToDeserializeToBeWritten<NewXML>(File.ReadAllText($"{rootFilePath}newXML.xml"));
result1.PrintResult();
result2.PrintResult();
}
static ResponseBase MethodToDeserializeToBeWritten<T>(string fileContent) where T : ResponseBase
{
return XML.Deserialize<T>(fileContent);
}
}
OUTPUT:
Result is of type 'OldXML': 101, 10
Result is of type 'NewXML': 101, 10, 10
Or you can get rid off MethodToDeserializeToBeWritten method and simplify your code as follows:
ResponseBase result1 = XML.Deserialize<OldXML>(File.ReadAllText($"{rootFilePath}oldXML.xml"));
ResponseBase result2 = XML.Deserialize<NewXML>(File.ReadAllText($"{rootFilePath}newXML.xml"));
Also note that you need to mark OldXML and NewXML with a XmlRoot attribute. Otherwise, you get an exception.

Deserialising JSON files in C#

I keep getting following error:
Object reference not set to an instance of an object
This is my Json string in a file at C:\part_param.json
{
"part_parameters" : {
"bar_diameter" : 300.4,
"core_height" : 132,
"roughing_offset" : 0.3
}
}
and the code I am using is as follows:
public class PLMpartParameter
{
public class Parameters
{
public float bar_diameter;
public float core_height;
public float roughing_offset;
public Parameters(float barD, float coreH, float roughingO)
{
bar_diameter = barD;
core_height = coreH;
roughing_offset = roughingO;
}
}
public Parameters parameters;
public PLMpartParameter(Parameters param)
{
parameters = param;
}
}
public static void LoadJson()
{
using (System.IO.StreamReader r = new System.IO.StreamReader(#"C:\part_param.json"))
{
string json = r.ReadToEnd();
_logger.Info(string.Format("Read entire file complete. File Values: {0}", json));
try
{
PLMpartParameter part = Newtonsoft.Json.JsonConvert.DeserializeObject<PLMpartParameter>(json);
}
catch (Exception e)
{
_logger.Info(string.Format("Read Json failed {0}", e.Message));
}
}
What am I missing here?
I think the problem is your property is called 'parameters' but in your json it's 'part_parameters'.
You must add a JSON attribute above your property, so that your properties are recognized when deserializing your object.
you will find an example just below
public class Parameters
{
[JsonProperty("bar_diameter")]
public float bar_diameter;
[JsonProperty("core_height")]
public float core_height;
[JsonProperty("roughing_offset")]
public float roughing_offset;
public Parameters(float barD, float coreH, float roughingO)
{
bar_diameter = barD;
core_height = coreH;
roughing_offset = roughingO;
}
}
Quoting Prasad Telkikar's answer as that fixed it right away
Use json2csharp to get model for your json file, then deserialize your
json. You can use visual studio in build function to create class i.e.
Edit -> Paste special -> Paste JSON as Class
Here is class
> public class PartParameters {
> public double bar_diameter { get; set; }
> public int core_height { get; set; }
> public double roughing_offset { get; set; } }
>
> public class RootObject {
> public PartParameters part_parameters { get; set; } }
To deserialize, use below code
PLMpartParameter part =
Newtonsoft.Json.JsonConvert.DeserializeObject(json);
My final code looks like this, and its working!!
public class PartParameters
{
public double bar_diameter { get; set; }
public int core_height { get; set; }
public double roughing_offset { get; set; }
}
public class RootObject
{
public PartParameters part_parameters { get; set; }
}
public static void LoadJson()
{
using (System.IO.StreamReader r = new System.IO.StreamReader(#"C:\part_param.json"))
{
string json = r.ReadToEnd();
try
{
RootObject part = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json);
_logger.Info(string.Format("list values : bardiameter: {0}, coreHeight: {1}, roughingOffset: {2}",
part.part_parameters.bar_diameter,part.part_parameters.core_height, part.part_parameters.roughing_offset));
}
catch (Exception e)
{
_logger.Info(string.Format("Read Json failed {0}", e.Message));
}
}
}
You have a few issues with your code:
You need to have default constructors for the classes (this is due to how serializers work with types - they are not going to understand your class-specific paramaterised constructor).
Your fields need to be settable properties (Just add {get;set;} and you should be good to go).
I would suggest you decorate the parameters property with [JsonProperty("part_parameters")] to get the deserialization
behaviour you're expecting.

DeserializeObject<T>(string s) in generic method in C# using Newtonsoft.JSON is not working

Currently I am just returning the json string to corresponding file from where Test1() is called and Deserializing there as ResponseClass r = JsonConvert.DeserializeObject(response_json)
Send part I forget to make a class [Serializable].Its working fine now.
Part1:
public class Movie
{
public string Name { get; set; }
public string Description { get; set; }
public string Classification { get; set; }
public string Studio { get; set; }
public DateTime? ReleaseDate { get; set; }
public List<string> Genres{ get; set; }
}
public class ResponseClass
{
public string SuccessStatus{ get; set; }
public string next_link { get; set; }
}
private void Test1<T,Q>()
{
string json = #"{
'Name': 'Bad Boys',
'ReleaseDate': '1995-4-7T00:00:00',
'Genres': [
'Action',
'Comedy'
]
}";
//Here making network call with above json and getting correct response_josn
Q response_obj = JsonConvert.DeserializeObject<Q>(reponse_json);
print(response_obj);
}
I am calling Test1() as follows on button click:
Test1<Movie, ResponseClass>();
For the above example I am getting print log as ClassName+Movie (T FullName).
I want to deserialize the string into that class. How to achieve that?
Part2 : If I have class as:
[Serializable]
public class Movie
{
public string Name;
public string Description;
public string Classification;
public string Studio;
public DateTime ReleaseDate;
public SubClass subClass;
public List<SubClass> lsubclass;
}
[Serializable] //This was the mistake.
public class SubClass
{
public string a;
public string b;
public List<string> ReleaseCountries;
}
private Movie createValidMovieJson()
{
Movie m = new Movie();
SubClass sc = new SubClass();
sc.a = "aaa";
sc.b = "bbb";
sc.ReleaseCountries = new List<string>();
sc.ReleaseCountries.Add("Japan");
sc.ReleaseCountries.Add("India");
List<SubClass> lsC = new List<SubClass>();
lsC.Add(sc);
lsC.Add(sc);
m.Name = "Bad Boys";
m.Studio = "Pixa";
m.subClass = sc;
m.lsubclass = lsC;
Debug.Log(JsonUtility.ToJson(m)); // value n log = {"Name":"Bad Boys","Description":"","Classification":"","Studio":"Pixa"}
return m;
}
JsonUtility is returning empty value in place of subclass after using ToJson() as shown in above function.
Based on the screenshot you added I think you are expecting to be able to treat the deserialized type as a Movie. This is the way to achieve that:
var movie = JsonConvert.DeserializeObject<Movie>(json);
Currently your deserialized object is being treated as type T - which could be anything since you have no generic type constraints on your method.
Like I said in the comment section, JsonUtility should do it.
I just replaced T m = JsonConvert.DeserializeObject(json); with T
m = JsonUtility.FromJson(json); it gives an error
ArgumentException: JSON parse error: Missing a name for object member.
Your json is invalid for JsonUtility. I believe you are using ' instead of ". This is why you are getting this error.
Use the function below to generate a valid json:
void createValidMovieJson()
{
Movie m = new Movie();
m.Name = "Bad Boys";
m.ReleaseCountries = new List<string>();
m.ReleaseCountries.Add("Japan");
m.Studio = "Pixa";
Debug.Log(JsonUtility.ToJson(m));
}
You will get:
{"Name":"Bad Boys","Description":"","Classification":"","Studio":"Pixa","ReleaseCountries":["Japan"]}
When ecaped for testing, you will get:
{\"Name\":\"Bad Boys\",\"Description\":\"\",\"Classification\":\"\",\"Studio\":\"Pixa\",\"ReleaseCountries\":[\"Japan\"]}
For JsonUtility to work, you must add [Serializable] to the class and remove { get; set; } from them class variables.
If your goal is to convert any json to any data type then you have to return generic type then use Convert.ChangeType to convert it to that type.
It should look something like this:
// Use this for initialization
void Start()
{
string json = "{\"Name\":\"Bad Boys\",\"Description\":\"\",\"Classification\":\"\",\"Studio\":\"Pixa\",\"ReleaseCountries\":[\"Japan\"]}";
Movie movie = Load<Movie>(json);
print(movie.Name);
}
[Serializable]
public class Movie
{
public string Name;
public string Description;
public string Classification;
public string Studio;
public DateTime? ReleaseDate;
public List<string> ReleaseCountries;
}
private T Load<T>(string json)
{
object resultValue = JsonUtility.FromJson<T>(json);
return (T)Convert.ChangeType(resultValue, typeof(T));
}

Add root element to json serialization in C#

I am creating a webservice to interact with a JSON API.
This API needs me to set a root element in the string, but I just cannot get this to happen.
The place where it all happens - right now just made to just show me the json output:
public static string CreateServiceChange(ServiceChange change)
{
string json = JsonConvert.SerializeObject(change);
return json;
}
This is the ServiceChange class:
public class ServiceChange
{
[JsonProperty("email")]
public string requesterEmail { get; set; }
[JsonProperty("description_html")]
public string descriptionHtml { get; set; }
[JsonProperty("subject")]
public string subject { get; set; }
[JsonProperty("change_type")]
public int changeType { get; set; }
}
And the method binding those two together:
public string copyTicketToChange(int ticketId)
{
HelpdeskTicket.TicketResponseActual ticket = getHelpdeskTicket(ticketId);
ServiceChange change = new ServiceChange();
change.descriptionHtml = ticket.Response.DescriptionHtml;
change.requesterEmail = ticket.Response.Email;
change.subject = ticket.Response.Subject;
change.changeType = 1;
string Response = Dal.CreateServiceChange(change);
return Response;
}
The json output looks like this right now:
{"email":"test#test.com","description_html":"This is a test","subject":"Testing","change_type":1}
And the expected output:
{ "itil_change": {"email":"test#test.com","description_html":"This is a test","subject":"Testing","change_type":1}}
How can I achieve this?
Wrap your ServiceChange into another object and serialize it:
public class ServiceChangeWrapper
{
public ServiceChange itil_change { get; set; }
}
...
public static string CreateServiceChange(ServiceChange change)
{
ServiceChangeWrapper wrapper = new ServiceChangeWrapper { itil_change = change};
string json = JsonConvert.SerializeObject(wrapper);
return json;
}

Categories