Getting DataContractSerializer error - c#

I'm not sure to what i'm doing wrong here but its now been a couple of days of me trying to solve this out but still no luck.
I have a model like this:
public class Event
{
public int Id {get;set;}
public string EventName {get;set;}
public DateTime StartTime{get;set;}
public DateTime FinishTime {get;set;}
}
Then i have a web Api method like this :
[HttpGet]
public IQueryable GetTimes()
{
var times = context.Events.where(x => x.EventName == "even name").Select(x => x new{
x.Id,
x.EventName,
StartTime = x.StartTime.ToShortTimeString(), // just getting time only
FinishTime = x.FinishTime.ToShortTimeString() // just getting time only
});
}
When I run my project I get the following error:
ArrayOf_x003C__x003E_f__AnonymousType0Ofintstringstringstringstringdoubledoublestring:http://schemas.datacontract.org/2004/07/'
is not expected. Consider using a DataContractResolver if you are
using DataContractSerializer or add any types not known statically to
the list of known types - for example, by using the KnownTypeAttribute
attribute or by adding them to the list of known types passed to the
serializer
I have never seen this error before and I'm not so sure on how to tackle this one.
I have also realized that when I run a test for instance if I do the following in that method:
x.Id,
x.EventName,
StartTime = "12.30", // this works fine and no error is thrown.
FinishTime = "16.30"
//also the following works fine:
x.Id,
x.EventName,
x.StartTime,
x.FinishTime, // this works fine obviously i only need the time only
How is this caused and how can I fix this?

The DataContractJsonSerializer cannot serialize anonymous types. Are you allowed to use another built-in serializer? In that case you can use the standard JSON serializer by setting your HttpConfiguration as follows:
httpConfiguration.Formatters.Clear();
httpConfiguration.Formatters.Add(new JsonMediaTypeFormatter());
You could also write a custom attribute to disable the DataContractJsonSerializer for a particular controller only:
public class UseDefaultJsonSerializerAttribute : Attribute, IControllerConfiguration
{
public void Initialize(
HttpControllerSettings controllerSettings,
HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Formatters.Clear();
controllerSettings.Formatters.Add(new JsonMediaTypeFormatter());
}
}

The problem is occurring because the DataContractSerializer on the client side ,used for deserializing the result, is expecting an array of a specific type and the server is handing out an array of an anonymous type. You need to use the same type on the server and client or 2 types that use the same Data Contract (which is how the generated proxy classes the VS creates work).
Try creating a Data Transfer Object (DTO) class to use on both client and server.
[DataContact]
public class EventDTO {
[DataMember]
public int Id { get; set; }
[DataMember]
public string EventName { get; set; }
[DataMember]
public TimeSpan StartTime { get; set; }
[DataMember]
public TimeSpan EndTime {get;set;}
}
Nowe your get method can look like
[HttpGet]
public IQueryable<EventDTO> GetTimes()
{
return context.Events.where(x => x.EventName == "even name")
.Select(x => x new EventDTO
{
Id = x.Id,
EventName = x.EventName,
StartTime = x.StartTime.TimeOfDay,
FinishTime = x.FinishTime.TimeOfDay
});
}
Make sure you use the same type for your Deserializer on the client

Related

Cannot dynamically create an instance of type 'System.Text.Json.Nodes.JsonObject'. Reason: No parameterless constructor defined.)

Basically, I have this class in my C# project:
public class Item
{
[BsonId]
public ObjectId Id { get; set; }
public string AppId { get; set; }
public JsonObject Fields { get; set; }
}
And I've created a couple of items in a Mongo DB using a POST request with this body:
{
"AppId":"(an already existing app id)",
"Fields":
{
"NombreCliente": "Pepillo",
"Email": "pepiko#email.com",
"Telefono": "pp56656784",
"Localidad": "Pepeland",
"Facturas": ["848435498","0564864984"]
}
}
And they were correctly created.
Problem occurs whenever I try to get these items. The error in the tittle pops up:
"(An error occurred while deserializing the Fields property of class divitiae_api.Models.Item: Cannot dynamically create an instance of type 'System.Text.Json.Nodes.JsonObject'. Reason: No parameterless constructor defined.)"
What should I do? I'm really lost here... I've tried creating a parameterless constructor but still keeps failing...
UPDATE
To create an Item I'm using the next method. I can't use a direct "Item" because it throws an exception regarding circular reference in the Field property, so I serialize it to a JSON string and then insert it like that into the collection:
public async Task InsertItem(JsonObject item)
{
var options = new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.Preserve
};
var jsonString = System.Text.Json.JsonSerializer.Serialize(item, options);
var document = BsonSerializer.Deserialize<BsonDocument>(jsonString);
var collection2 = _repository.db.GetCollection<BsonDocument>("Items");
await collection2.InsertOneAsync(document);
}
And this is the one I'm using to GET them:
public async Task<List<Item>> GetAllAppItems(string appId)
{
var filter = Builders<Item>
.Filter
.Eq(s => s.AppId, appId);
return await Collection.FindAsync(new BsonDocument()).Result.ToListAsync();
}
Think that MongoDB .NET Driver is unable to know how to map an object to JsonObject as JsonObject is not under its library.
An easy fix is to modify the Fields data type to either object or dynamic.
public class Item
{
...
public dynamic Fields { get; set; }
}

How to fix error Cannot deserialize the current JSON object? [duplicate]

Below is a (slightly) stripped down response I get from a REST API upon successful creation of a new "job code" entry. I need to deserialize the response into some classes, but I'm stumped.
For reference, I'm using JSON.NET in .NET 3.5 (running in a SSIS script in SQL Server 2008 R2) to attempt my deserialization. Here's the JSON - which I obviously have no control over as it's coming from someone else's API:
{
"results":{
"jobcodes":{
"1":{
"_status_code":200,
"_status_message":"Created",
"id":444444444,
"assigned_to_all":false,
"billable":true,
"active":true,
"type":"regular",
"name":"1234 Main Street - Jackson"
},
"2":{
"_status_code":200,
"_status_message":"Created",
"id":1234567890,
"assigned_to_all":false,
"billable":true,
"active":true,
"type":"regular",
"name":"4321 Some Other Street - Jackson"
}
}
}
}
In my C# code, I do have a "JobCode" class defined which only partially maps the JSON values to properties - I'm not interested in all of the data that's returned to me:
[JsonObject]
class JobCode
{
[JsonProperty("_status_code")]
public string StatusCode { get; set; }
[JsonProperty("_status_message")]
public string StatusMessage { get; set; }
[JsonProperty("id")]
public string Id {get; set;}
[JsonProperty("name")]
public string Name { get; set; }
//-------------------------------------------------------------------------------
// Empty constructor for JSON serialization support
//-------------------------------------------------------------------------------
public JobCode() { }
}
I'm attempting to deserialize the data via this call:
newResource = JsonConvert.DeserializeObject<JobCode>(jsonResponse);
Where jsonResponse is the code outputted above.
When I execute the code, "newResource" always comes back as null - which is not unexpected because I know that there are actually multiple jobcodes in the data and this code is trying to deserialize it into a single JobCode object. I tried creating a new class called "JobCodes" that looks like this:
class JobCodes
{
[JsonProperty("jobcodes")]
public List<JobCode>_JobCodes { get; set; }
}
And then I tried calling this:
newResource = JsonConvert.DeserializeObject<JobCodes>(jsonResponse);
But the issue persists - my return object is null.
What's throwing me off, I think, is the presence of the "1" and "2" identifiers. I don't know how to account for their presence in my object design and/or usage of the JSON.NET class / property attributes like [JsonObject],[JsonProperty], etc.
When I run the JSON data through JSON2CSharp, it constructs some weird-looking classes, so that hasn't proven too effective. I've validated the JSON with several different validators and it all checks out - I just don't know what I'm missing here.
Ultimately, I'd like to return a List from the JSON data, but I'm stumped on what I need to do to make that happen.
Your problem is twofold:
You don't have a class defined at the root level. The class structure needs to match the entire JSON, you can't just deserialize from the middle.
Whenever you have an object whose keys can change, you need to use a Dictionary<string, T>. A regular class won't work for that; neither will a List<T>.
Make your classes like this:
class RootObject
{
[JsonProperty("results")]
public Results Results { get; set; }
}
class Results
{
[JsonProperty("jobcodes")]
public Dictionary<string, JobCode> JobCodes { get; set; }
}
class JobCode
{
[JsonProperty("_status_code")]
public string StatusCode { get; set; }
[JsonProperty("_status_message")]
public string StatusMessage { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
Then, deserialize like this:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
Working demo here
Excellent Answers!
For those out there that may need some more help with the JSON Class Configuration, try: http://json2csharp.com/#
An excellent way of Auto Generating the Classes!
Or even easier, in VS, Goto:
Edit -> Paste Special -> Paste as JSON Classes
Because you can't change the scheme of JSON, and you can't set constant No. of properties, I'd suggest you to use JObject
var jobject = JObject.Parse(json);
var results = jobject["results"];
var jobcodes = results["jobcodes"];
var output = jobcodes.Children<JProperty>()
.Select(prop => prop.Value.ToObject<JobCode>())
.ToList();
Warning: code assumes, that JSON is always in proper schema. You should also handle invalid schema (for example where property is not of JobCode scheme).
You can also deserialize your json to an object of your target class, and then read its properties as per normal:
var obj = DeSerializeFromStrToObj<ClassToSerialize>(jsonStr);
Console.WriteLine($"Property: {obj.Property}");
where DeSerializeFromStrToObj is a custom class that makes use of reflection to instantiate an object of a targeted class:
public static T DeSerializeFromStrToObj<T>(string json)
{
try
{
var o = (T)Activator.CreateInstance(typeof(T));
try
{
var jsonDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
var props = o.GetType().GetProperties();
if (props == null || props.Length == 0)
{
Debug.WriteLine($"Error: properties from target class '{typeof(T)}' could not be read using reflection");
return default;
}
if (jsonDict.Count != props.Length)
{
Debug.WriteLine($"Error: number of json lines ({jsonDict.Count}) should be the same as number of properties ({props.Length})of our class '{typeof(T)}'");
return default;
}
foreach (var prop in props)
{
if (prop == null)
{
Debug.WriteLine($"Error: there was a prop='null' in our target class '{typeof(T)}'");
return default;
}
if (!jsonDict.ContainsKey(prop.Name))
{
Debug.WriteLine($"Error: jsonStr does not refer to target class '{typeof(T)}'");
return default;
}
var value = jsonDict[prop.Name];
Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object safeValue = value ?? Convert.ChangeType(value, t);
prop.SetValue(o, safeValue, null); // initialize property
}
return o;
}
catch (Exception e2)
{
Debug.WriteLine(e2.Message);
return o;
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return default;
}
}
A complete working example class can be found in my enhanced answer to a similar question, here

Marking a property as Json Required with Newtonsoft just makes object null

I have a request object for my API and I want to enforce that my properties are always present in the body. Here is my object:
public class Code
{
[JsonProperty("id", Required = Required.Always)]
public string id { get; set; }
[JsonProperty("type", Required = Required.Always)]
public string type { get; set; }
}
However, when I don't pass in the type property in my request body, my Code object is null. Instead I would want a bad request error to get propagated back to the client. Will the Newtonsoft decorator not do that for me here? Or would I have to manually add checks to see if the properties are not null?
This following code throws for me as expected:
string serialized = #"{ 'noId': '123' }";
Code deserialized = JsonConvert.DeserializeObject<Code>(serialized);
Console.WriteLine(deserialized.Id);
My code class:
class Code
{
[JsonProperty("id", Required = Required.Always)]
public string Id { get; set; }
}
Can you confirm Newtonsoft.Json is used? If you are using ASP.NET Core 3.x or above, please refer to How to use Newtonsoft.Json as default in Asp.net Core Web Api? to set your project up to use Newtonsoft.Json.
Fluent Validation is the solution for you. So, you don't have to use JsonProperty attributes.
Usage:
First, create a validator for your class.
public class CodelValidator : AbstractValidator<Code>
{
public CodelValidator()
{
RuleFor(x => x.id).NotEmpty().WithMessage("id is required.");
RuleFor(x => x.type).NotEmpty().WithMessage("type is required.");
}
}
Inside your controller method:
public ActionResult TestMethod(Code code)
{
var validator = new CodeValidator();
var validationResult = await validator.ValidateAsync(code);
if (validationResult.IsValid == false)
{
var errorMessages = validationResult.Errors.Select(s => s.ErrorMessage);
// manage how you want to show the erros.
}
...
}
So, as you get all the errors. Now you can show however you want.
https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1064
Currently the value for JsonProperty.Required only determines if the value is required - it does not allow you to indicate that a value may or may not be null.
Also in looking at the code it looks like all empty strings get converted to null
https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs#L282

Managing multiple versions of object in JSON

I have a class in C#, that has a number of variables. Let's call it "QuestionItem".
I have a list of this object, which the user modifies, and then sends it via JSON serialization (with Newtonsoft JSON library) to the server.
To do so, I deserialize the objects that are already in the server, as a List<QuestionItem>, then add this new modified object to the list, and then serialize it back to the server.
In order to display this list of QuestionItems to the user, I deserialize the JSON as my object, and display it somewhere.
Now, the problem is - that I want to change this QuestionItem and add some variables to it.
But I can't send this NewQuestionItem to the server, because the items in the server are of type OldQuestionItem.
How do I merge these two types, or convert the old type to the new one, while the users with the old version will still be able to use the app?
You are using an Object Oriented Language, so you might aswell use inheritance if possible.
Assuming your old QuestionItem to be:
[JsonObject(MemberSerialization.OptOut)]
public class QuestionItem
{
[JsonConstructor]
public QuestionItem(int Id, int Variant)
{
this.Id = Id;
this.Variant = Variant;
}
public int Id { get; }
public int Variant { get; }
public string Name { get; set; }
}
you can extend it by creating a child class:
[JsonObject(MemberSerialization.OptOut)]
public class NewQuestionItem : QuestionItem
{
private DateTime _firstAccess;
[JsonConstructor]
public NewQuestionItem(int Id, int Variant, DateTime FirstAccess) : base(Id, Variant)
{
this.FirstAccess = FirstAccess;
}
public DateTime FirstAccess { get; }
}
Note that using anything different than the default constructor for a class requires you to use the [JsonConstructor] Attribute on this constructor and every argument of said constructor must be named exactly like the corresponding JSON properties. Otherwise you will get an exception, because there is no default constructor available.
Your WebAPI will now send serialized NewQuestionItems, which can be deserialized to QuestionItems. In fact: By default, JSON.NET as with most Json libraries, will deserialize it to any object if they have at least one property in common. Just make sure that any member of the object you want to serialize/desreialize can actually be serialized.
You can test the example above with the following three lines of code:
var newQuestionItem = new NewQuestionItem(1337, 42, DateTime.Now) {Name = "Hello World!"};
var jsonString = JsonConvert.SerializeObject(newQuestionItem);
var oldQuestionItem = JsonConvert.DeserializeObject<QuestionItem>(jsonString);
and simply looking at the property values of the oldQuestionItem in the debugger.
So, this is possible as long as your NewQuestionItem only adds properties to an object and does neither remove nor modify them.
If that is the case, then your objects are different and thus, requiring completely different objects with a different URI in your API, as long as you still need to maintain the old instance on the existing URI.
Which brings us to the general architecture:
The most clean and streamline approach to what you are trying to achieve is to properly version your API.
For the purpose of this link I am assuming an Asp.NET WebApi, since you are handling the JSON in C#/.NET. This allows different controller methods to be called upon different versions and thus, making structural changes the resources your API is providing depending on the time of the implementation. Other API will provide equal or at least similar features or they can be implemented manually.
Depending on the amount and size of the actual objects and potential complexity of the request- and resultsets it might also be worth looking into wrapping requests or responses with additional information. So instead of asking for an object of type T, you ask for an Object of type QueryResult<T> with it being defined along the lines of:
[JsonObject(MemberSerialization.OptOut)]
public class QueryResult<T>
{
[JsonConstructor]
public QueryResult(T Result, ResultState State,
Dictionary<string, string> AdditionalInformation)
{
this.Result = result;
this.State = state;
this.AdditionalInformation = AdditionalInformation;
}
public T Result { get; }
public ResultState State { get; }
public Dictionary<string, string> AdditionalInformation { get; }
}
public enum ResultState : byte
{
0 = Success,
1 = Obsolete,
2 = AuthenticationError,
4 = DatabaseError,
8 = ....
}
which will allow you to ship additional information, such as api version number, api version release, links to different API endpoints, error information without changing the object type, etc.
The alternative to using a wrapper with a custom header is to fully implement the HATEOAS constraint, which is also widely used. Both can, together with proper versioning, save you most of the trouble with API changes.
How about you wrapping your OldQuestionItem as a property of QuestionItem? For example:
public class NewQuestionItem
{
public OldQuestionItem OldItem { get; set; }
public string Property1 {get; set; }
public string Property2 {get; set; }
...
}
This way you can maintain the previous version of the item, yet define new information to be returned.
Koda
You can use something like
public class OldQuestionItem
{
public DateTime UploadTimeStamp {get; set;} //if less then DateTime.Now then it QuestionItem
public string Property1 {get; set; }
public string Property2 {get; set; }
...
public OldQuestionItem(NewQuestionItem newItem)
{
//logic to convert new in old
}
}
public class NewQuestionItem : OldQuestionItem
{
}
and use UploadTimeStamp as marker to understand, what Question is it.

Serializing published content in umbraco

I am currently writing an api for a custom application my company is writing. Part of this involves getting published content out in JSON format. When I try serializing ipublishedcontent directly it obviously attempts to serialize all of the umbraco data and relations that I simply don't need (in fact it fails with a stack overflow). Is there a way to get just the custom properties from an item of content without specifying the fields?
I am using webapi and passing it objects to serialize itself and I'm using a dynamic to manually specify the fields. The Product type which I'm initially selecting into is from modelsbuilder. My code currently looks a little like this:
public object Get(string keywords = "")
{
// Get Data from Umbraco
var allProducts = Umbraco.TypedContent(1100).Children.Select(x => new Product(x));
if (keywords != "")
{
allProducts = allProducts.Where(x => x.Name.Contains(keywords));
}
return allProducts.Select(x => new
{
id = x.Id,
name = x.Name,
price = x.Price
});
}
It seems to me that there should be a simple way to do this without having to create a dynamic with just the fields I want but I can't work it out. I just don't want to have to change my code every time the document type in umbraco changes!
You can use Ditto to map your data into an object.
Create an object with properties that match the alias's of your fields (case insensitive)
public class Product{
public int id {get;set;}
public string name {get;set;}
public string price {get;set;}
}
Then map a single or collection of IPublishedContent objects using .As
return allProducts.As<Product>();
You can use the UmbracoProperty attribute to specify the alias too if it is different than you need for your json or use the JsonProperty attribute to change the name on serialize.
Take a look at the code in the MemberListView - it does a similar thing while retrieving Members without knowing in advance what the properties on the MemberType will be:
https://github.com/robertjf/umbMemberListView/blob/master/MemberListView/Models/MemberListItem.cs
For example:
[DataContract(Name = "content", Namespace = "")]
public class MemberListItem
{
// The following properties are "known" - common to all IPublishedContent
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "contentType")]
public IContentType ContentType { get; set; }
// This one contains a list of all other custom properties.
private Dictionary<string, string> properties;
[DataMember(Name = "properties")]
public IDictionary<string, string> Properties
{
get
{
if (properties == null)
properties = new Dictionary<string, string>();
return properties;
}
}
}
MemberListView converts to this from a list of SearchResult using AutoMapper, but you could just as easily map it from IPublishedContent.

Categories

Resources