JsonConvert.DeserializeObject() does not work [duplicate] - c#

I have problem with JsonConvert deserializer. I have class
[BsonCollection("matches")]
public class MatchData : Document
{
[JsonPropertyName("id")]
public string ExternalMatchId { get; set; }
...
}
In my controller, I am trying to deserialize in this way:
[HttpPost("end")]
public ActionResult RoundEnd([FromBody] dynamic data)
{
var saveData = JsonConvert.DeserializeObject<MatchData>(data.ToString());
...
}
Input JSON looks like
"{"id": "61696f268c7b70b5f3e85803",
"game_server_id": "615ed4a1cd95e8209a4ab67d",
...
But in my output MatchData object ExternalMatchId is null. How to fix that?

You are mixing frameworks here. The JsonPropertyName attribute is for the System.Text.Json namespace whereas you are using JSON.Net to deserialise. So the solution is to stick with one of them. Either deserialise with the built in framework:
System.Text.Json.JsonSerializer.Deserialize<MatchData>(data.ToString());
Or switch the attributes to use the JSON.Net versions:
[JsonProperty("Foo")]
Having said all that, it looks like you can simplify the whole thing by getting MVC to do the work for you. Instead of using dynamic as the model (don't do that - dynamic is problematic and every time you use it a kitten dies), put the model in here directly:
public ActionResult RoundEnd([FromBody] MatchData data)

Related

Why JsonConvert.DeserializeObject ignoring JsonPropertyName attributes?

I have problem with JsonConvert deserializer. I have class
[BsonCollection("matches")]
public class MatchData : Document
{
[JsonPropertyName("id")]
public string ExternalMatchId { get; set; }
...
}
In my controller, I am trying to deserialize in this way:
[HttpPost("end")]
public ActionResult RoundEnd([FromBody] dynamic data)
{
var saveData = JsonConvert.DeserializeObject<MatchData>(data.ToString());
...
}
Input JSON looks like
"{"id": "61696f268c7b70b5f3e85803",
"game_server_id": "615ed4a1cd95e8209a4ab67d",
...
But in my output MatchData object ExternalMatchId is null. How to fix that?
You are mixing frameworks here. The JsonPropertyName attribute is for the System.Text.Json namespace whereas you are using JSON.Net to deserialise. So the solution is to stick with one of them. Either deserialise with the built in framework:
System.Text.Json.JsonSerializer.Deserialize<MatchData>(data.ToString());
Or switch the attributes to use the JSON.Net versions:
[JsonProperty("Foo")]
Having said all that, it looks like you can simplify the whole thing by getting MVC to do the work for you. Instead of using dynamic as the model (don't do that - dynamic is problematic and every time you use it a kitten dies), put the model in here directly:
public ActionResult RoundEnd([FromBody] MatchData data)

Changing the entity framework templates

I have some problems while using Entity Framework.
I always use a JSONHelper class to convert a list of string to JSON.
Here is the JSONHelper Class:
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(obj);
}
}
This class creates JSON. But sometimes it gives me an error saying that "A circular reference was detected in Entity..........."
So in some of the properties of the classes that entity-framework created for me, I have to use [ScriptIgnore] Attribute. Then it works fine.
Now, let's say I made a small change in database. So I will refresh my ADO.Net Entity Data Model. Then I again have to open all those class files and write [ScriptIgnore].
So, I decided to make a change in template designer. But I could not find the files that entity framework uses to create my POCO classes.
Any Ideas????
Please don't tell me to change the way of converting List to JSON.
I recommend that you use the NewtonSoft.Json serializer. It works very well and has settings options for handling many things, including circular references. There is a lot of good documentation and help available for that serializer. Don't reinvent the wheel.
Circular references are controlled like this:
SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
I believe this answer describes what you need to do.
For any entity which you need to apply the ScriptIgnore attribute, you can make a partial class which does the attribute decoration.
Say your entity is like this
public partial class Entity
{
public int Id { get; set; }
public string Name { get; set; }
}
You can create a separate partial class like this
public interface IEntity
{
[ScriptIgnore]
string Name { get; set; }
}
public partial class Entity:IEntity
{
}
You can achieve this by modifying the T4 template file (.tt file). Take a look at the .tt file in a text editor and you will be able to see where your properties are being created. Insert the [ScriptIgnore] attribute in your template and it will be automatically included when you re-generate your entities.
You might need to include an appropriate 'using' at the top of the template etc.
You can do a lot by editing the T4 template, we used it to automatically have our entities implement INotifyPropertyChanged for all properties.

handling JSON response that is not the same each time

i am working with json.net to deserialize json -> C# objects.
it works great in most cases but there are times where rather than getting an array i get object.
so my class (generated using http://json2csharp.com/ and modified to add property).
where i more than 1 arrival methods (such as pick up or ship) it works fine.
public class ArrivalMethods
{
[JsonProperty(PropertyName = "ArrivalMethod")]
public List<string> ArrivalMethod { get; set; }
}
but it breaks where there only 1 arrival method in the json response from my service because i believe json.net is expecting an object like below.
public class ArrivalMethods
{
[JsonProperty(PropertyName = "ArrivalMethod")]
public string ArrivalMethod { get; set; }
}
i am new to json etc. so i am not sure what i am doing wrong. but this throws exception.
Error converting value \"Ship\" to type 'System.Collections.Generic.List`1[System.String]'.
Path 'ProductDetail.SoftHardProductDetails.Fulfillment.
ArrivalMethods.ArrivalMethod', line 1, position 113."
here is where it breaks:
"ArrivalMethods":{
"ArrivalMethod":"Ship"
},
Your JSON should contain [] where there is a list. And if I'm understanding your answer correct, the first class example is the way you want to end up? If so you need the JSON on the bottom to mark ArrivalMethod as a list, wich its not now.
"ArrivalMethods":
{
"ArrivalMethod":["Ship"]
},
To be honest i get a little confused when there is a list with no plural ending.
If you can change the response format that would be the best. It really looks like XML semantics converted to JSON...
If you can't change the response format, you can try to use JsonConverterAttribute to create custom converters for those properties.

MVC 4 ApiController not serializing System.Json.JsonObject properly

So I am new to working with MVC4 and the serialization of objects on the back end seems pretty "magical" to me so if I am doing this the wrong way please let me know.
My goal however is to build a simple rest API and return JSON out. I figured that I would use System.Json and just return JsonObject. I have simplified this down for the sake of this question but the objects are much more complicated in my real issue.
Here is my controller....
....
public class ActionsController : ApiController
{
// GET api/actions
public JsonObject Get()
{
JsonObject testObjet = new JsonObject();
testObjet.Add("Name", "Test name");
testObjet.Add("Description", "Test Description");
return testObjet;
}
....
I would expect to see:
{"Name":"Test name","Description":"Test Description"}
Instead I see:
{"Name":[],"Description":[]}
I actually seem to get better results when I return a string of the JsonObject or heck even just return the object itself with the exception it has enums and I want to return the names not the number values, which is what led me to JsonObject for customization.
Does anyone know why it is dropping off the values?
EDIT:
So because of Dan's comments below I tried just for giggles to see what the XML serializer spit out with the JSON object and I get the below exception...
"Type 'System.Json.JsonPrimitive' with data contract name 'JsonPrimitive:http://schemas.datacontract.org/2004/07/System.Json' is not expected."
So it appears that you can not serialize the System.Json.JsonObject object, because it uses a type that it does not expect.
That is shocking. Does anyone have a workaround? If not I am off to find out how to show enum names when serializing instead of values.
So the answer is apparently... You Can't!
It appears that the type JsonPrimitive is not supported to serialize objects. The answers provided below by Obi and Dan helped me to poke around a bit more and find out that the XML serializer actually throws an exception while the JSON serializer simply eats it and puts out an empty array which is what you see above.
There are any number of correct answers here.
Make your own custom serializer
Output JSON as a string
Return custom objects and then work around things like the Enum
values
I am sure there are others.
But whatever you do don't try to use System.Json as a return in the ApiController because you will get the results above.
You should not force your WebApi call to use a particular format like JSON. One of the features of WebApi is that you can specify the format as part of the request. Return an object from your Get call, and let the WebApi engine do the serialization and deserialization:
public class DataObject
{
public string Name { get; set; }
public string Description { get; set; }
}
public class ActionsController : ApiController
{
// GET api/actions
public DataObject Get()
{
var testObject = new DataObject
{
Name = "Test name",
Description = "Test Description"
};
return testObject;
}
}
You can specify the format by setting the Accept header to application/xml, application/json, etc.
The default JSON serializer has no problem serializing simple string properties like Name and Description.
I would suggest you did this instead
// GET api/actions
public object Get()
{
//New up a strongly typed object if you want to return a specific type
//and change Action return type accordingly
var testObjet = new (){
Name= "Test name",
Description= "Test Description"
};
return testObjet;
}
Dan has posted a similar answer below so let me try to address your other problem. To serialize the enum, I would suggest you hide it in a public string property which would return the string value of the enum,
public class DataObject{
public MyEnum SomeEnumValue;
public string EnumValue{
get {
//..return SomeEnumValue string value
}
}
}
You can then read the value from EnumValue which should be properly serialized as you want.

Circular reference with EF4

I am trying to return an Entity Framework 4 object with children to an jQuery JSON AJAX function but I get a circular reference error - in short my method looks like this
[WebMethod]
public static JSONObject Get()
{
WebHelper.JSONObject lJSONObject = new WebHelper.JSONObject();
lJSONObject.Object = Repository.Parent.Include("Child.Child").FirstOrDefault();
return lJSONObject;
}
if I do not include children the functions works fine, but with children the circular reference occurs. Any ideas what I can do to fix this?
Do you have to return a JSONObject? If not, you can try using Json.NET, which will handle circular references properly:
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
JsonConvert.SerializeObject(object, Formatting.Indented, settings);
My guess is that the JsonObject is simply a wrapper that will serialize the entity and put it on the response stream, which is simple enough to do manually.
Try adding ScriptIgnore attribute to property Parent. See for more details: http://msdn.microsoft.com/en-us/library/system.web.script.serialization.scriptignoreattribute.aspx
-- EDIT --
They will be overwritten if you do this in designer file. But you can try adding metadata type:
[MetadataType(typeof(TestMD))]
public partial class Test
{
}
public class TestMD
{
[ScriptIgnore]
public object Parent { get; set; }
}
I had the same problem. Not sure if there's any other solution, but I got it to work creating my own Serialize method.

Categories

Resources