DataContract Name Attribute not Working with Json Serializer - asp.net

I'm building a JSON only WepApi and I have a controller which returns a list.
I have decorated the list with Datacontract/Datamember attributes like this:
[DataContract(Name = "Files", Namespace = "http://schemas.example.com")]
public class FileDesc
{
[DataMember(Name = "MyFileName")]
public string Name { get; set; }
[DataMember(Name = "MyFilePath")]
public string Path { get; set; }
[DataMember(Name = "MyFileSize")]
public long? Size { get; set; }
public FileDesc(string n, string p, long? s)
{
Name = n;
Path = p;
Size = s;
}
}
In my controller I return the list like this:
// Response
List<FileDesc> FileList = new List<FileDesc>();
foreach (MultipartFileData file in provider.FileData)
{
FileList.Add(new FileDesc(file.Headers.ContentDisposition.FileName, file.LocalFileName , file.Headers.ContentDisposition.Size ));
}
return FileList;
But in the JSON output the Name attribute for the list is missing:
[
{
"myFileName": "\"ArduinoUNO.png\"",
"myFilePath": "c:\\images\\ArduinoUNO.png",
"myFileSize": null
}
]
To force JSON-only output i have removed the xml formatter on Global.asax:
//Only JSON
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
Thanks in advance

Arrays don't have names by themselves in JSON. If you want a name on the list, you need an outer object that has a named property to hold the list.
Something like this:
public class Response
{
[DataMember(Name = "Files")]
List<FileDesc> FileList { get; set; }
}
The JSON would then look like this:
{
"Files":
[
{
"myFileName": "\"ArduinoUNO.png\"",
"myFilePath": "c:\\images\\ArduinoUNO.png",
"myFileSize": null
}
]
}

Related

Optimal way to store objects in AppConfig.json

My AppConfig.json:
{
"MyTimeZone: "CET",
"RegularString" : "SomeValue",
"AnArray" : ["1","2"]
}
My POCO class:
public class Settings
{
public TimeZoneInfo MyTimeZone { get; set; }
public string RegularString { get; set; }
public IList<string> AnArray { get; set; }
}
Registry.cs:
var configuration = GetConfiguration("AppSettings.json");
services.Configure<Settings>(configuration.GetSection("Settings"));
This of course does not bind "CET" into a valid TimeZoneInfo object. Now the question is what is the best place in my application (a web app) to convert from string to TimeZoneInfo? Is there a way to automatically convert string config values to objects based on certain rules without creating custom converters?
Reference Use DI services to configure options
services.AddOptions<Settings>()
.Configure<IConfiguration>((setting, configuration) => {
var section = config.GetSection("Settings");
//This will populate the other properties that can bind by default
section.Bind(setting);
//this will extract the remaining value and set it mnually
string value = section.GetValue<string>("MyTimeZone");
TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(value);
setting.MyTimeZone = info;
});
The complex setting value can be extracted directly from configuration via DI and used to create the time zone and apply it to the settings.
It just my personal opinion but I prefer the MyTimeZone to be a json object instead only a string. Consider the following:
"Settings": {
"MyTimeZone": {
"ConfigureTimeZoneById": "CET"
},
"RegularString": "SomeValue",
"AnArray": [ "1", "2" ]
}
MyTimeZone.ConfigureTimeZoneById is not part of the actual data object. It's just proxy to bind object to configuration. This how TimeZone class might look like:
public class TimeZone
{
private string configureTimeZoneById { get; set; }
public string ConfigureTimeZoneById
{
get { return configureTimeZoneById; }
set
{
configureTimeZoneById = value;
InitializeTimeZone(value);
}
}
public string TimeZoneId { get; set; }
public string OtherProperties { get; set; }
private void InitializeTimeZone(string id)
{
var getTimeZone = TimeZonesDataset().FirstOrDefault(tzon => tzon.TimeZoneId.Equals(id));
if (getTimeZone != null)
{
this.TimeZoneId = getTimeZone.TimeZoneId;
this.OtherProperties = getTimeZone.OtherProperties;
}
}
//dummy dataset
private List<TimeZone> TimeZonesDataset() => new List<TimeZone> {
new TimeZone{TimeZoneId = "CET", OtherProperties = "Dummy properties to prove point"},
new TimeZone{TimeZoneId = "GMT", OtherProperties = default},
};

System.Text.Json Deserialize Fails

With this DTO:
public class QuestionDTO {
public Guid Id { get; set; }
public string Prompt { get; set; }
public List<Answer> Choices { get; set; }
public QuestionDTO() {
}
public QuestionDTO(Question question) {
this.Id = question.Id;
this.Prompt = question.Prompt;
this.Choices = question.Choices;
}
}
I was getting an error about Unable to Parse without a parameterless constructor. I have since fixed that, but now my objects are de-serialized empty:
using System.Text.Json;
var results = JsonSerializer.Deserialize<List<QuestionDTO>>(jsonString);
The jsonString contains 3 items with the correct data, and the deserialized list contains 3 items, but all the properties are empty.
The new json library is case sensitive by default. You can change this by providing a settings option. Here is a sample:
private JsonSerializerOptions _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
private async Task SampleRequest()
{
var result = await HttpClient.GetStreamAsync(QueryHelpers.AddQueryString(queryString, queryParams));
_expenses = await JsonSerializer.DeserializeAsync<List<Common.Dtos.Expenses.Models.Querys.ExpensesItem>>(result, _options);
}

Deserialize JSON array with Json.NET

How can I deseralize this JSON to C# collection with Json.NET ?
{
"3396": [{
"id": 767570,
"t": {
"0-43": [{
"id": 71968108,
"n": "No",
"v": 1.55,
"bt": 1
}, {
Create a model in C#:
public class YourModelJSON
{
[JsonProperty("yourmodel")]
public YourModel YourModel { get; set; }
}
public class YourModel
{
[JsonProperty("id")]
public string myId{ get; set; }
[JsonProperty("t")]
public string myT { get; set; }
...
}
And then deserialize your json with:
JsonConvert.DeserializeObject<List<YourModelJson>>(json);
I think you will have a hard time putting this json into classes, so I'd go for the dynamic solution here:
dynamic result = JsonConvert.DeserializeObject(json); // json -> escaped string
var array3396 = result["3396"];
var arrayT = array3396[0]["t"];
var array043 = arrayT["0-43"][0];
var id = array043["id"]; // output: 71968108
Made it as follows:
dynamic data = h.GetDynamicJSON(urlDet);
if (data != null)
{
foreach (var d in data)
{
var json = JsonConvert.SerializeObject(d.Value);
var obj = JsonConvert.DeserializeObject<List<Details>>(json);
}
}
Got source as dynamic object, then serialized 'dynamic named' value, and deserialized it to strong type object.

How to parse attribute in JsonConvert.DeserializeObject

We have following JSON output from an API.
We are using the following code in ASP.NET to deserialize a JSON response to a class:
var j = JsonConvert.DeserializeObject<classname>(response);
While deserializing the following JSON, we are unable to get the attribute values.
{
"User": {
"Address": {
"#CityName": "Test",
"$": "B4C99EB0-18E6-439F-882A-9E4A11E1FF75"
}
}
}
As per the above example we need to get #cityname and $ attribute values.
We need following output in the class:
#CityName : Test
$ : B4C99EB0-18E6-439F-882A-9E4A11E1FF75
I am getting the element value, but I am not able to get the attribute values.
Is there a way I can do this?
You can use the [JsonProperty] attribute to map non-standard JSON property names to properties in your class. Make your classes like this:
public class RootObject
{
public User User { get; set; }
}
public class User
{
public Address Address { get; set; }
}
public class Address
{
[JsonProperty("#CityName")]
public string CityName { get; set; }
[JsonProperty("$")]
public string Guid { get; set; }
}
Then deserialize like this:
var obj = JsonConvert.DeserializeObject<RootObject>(response);

Unwanted tag names in WCF Services

I have a wcf service and I have class like below:
public class Message
{
[XmlElement(ElementName = "message")]
[DataMember(Name = "message")]
public string message { get; set; }
[XmlElement(ElementName = "MsgID")]
[DataMember(Name = "MsgID")]
public string MsgID{ get; set; }
}
Then i browsed my service in a browser the xml i am getting like below:
<MessageResponse>
<Status>SUCCESS</Status>
<a:Message>
<a:message>msg1</a:message>
<a:MsgID>1</a:MsgID>
</a:Message>
</MessageResponse>
My Service Contract is Like below:
and one more important thing is:MessageDco class is a copy of Message Class
[ServiceContract()]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class UserLoginProfileService
{
[WebGet(UriTemplate = "/GetMessages?MsgID={MsgID}")]
public MessageResponse GetMessage(Guid MsgID)
{
MessageResponse objMessageResponse = new MessageResponse();
try
{
Message objMessage = new MessageFacade().GetMessage(MsgID);
if (objUserLoginProfile != null)
{
MessageResponse.Status = Status.Success;
MessageResponse.Messages = Mapper.ToDataContractObjects(Message);
}
else
{
objMessageResponse.Status = Status.Success;
}
}
catch (Exception ex)
{
objMessageResponse.Status = Status.Failure;
}
return objMessageResponse;
}
}
public class MessageResponse
{
[XmlElement(ElementName = "Messages")]
[DataMember(Name = "Messages")]
public MessageDco[] Messages { get; set; }
[XmlElement(ElementName = "Status")]
[DataMember(Name = "Status")]
public string Status { get; set; }
}
My mapper class is like below:
public sealed class Mapper
{
public static MessageDco[] ToDataContractObjects(IEnumerable<MessageDco> objMessageDco)
{
if (objMessageDco != null)
{
return objMessageDco.Select(a => ToDataContractObjects(a)).ToArray();
}
else
{
return null;
}
}
}
if we see "a:" is added to the tags. why it is added? i am unable remove that "a:" from the tags. Please help me how to remove "a:" from Tags. And also if you see Status tag is coming correct.
Thanks in advance.
If you're only using the DataContractSerializer you don't need the XmlElement attribute
Further more you should use a DataContract attribute on all classes and specify the namespaces, because they are defined implicitly if they are missing. Different classes may have difference XML namespaces.
The MessageResponse object has a Messages array of MessageDco object but the sample XML supplied does not match, I would expect a Messages element containing a set of Message elements.

Resources