Deserialize child elements using xmlserializer - c#

I'm trying to deserialize a xml file of this strucuture, but when I call this method
XmlSerializer(responseType).Deserialize(new MemoryStream(responseData))
none of the PricingQuote Child elements come through
<Pricing>
<Code>Success</Code>
<PricingQuotes>
<PricingQuote>
<ProductName>Conforming 30 Year Fixed</ProductName>
</PricingQuote>
<PricingQuote>
<ProductName>Conforming 20 Year Fixed</ProductName>
</PricingQuote>
</PricingQuotes>
</Pricing>

You need to make sure that your class definitions match the incoming XML. The ones below do that, and the deserialization works as expected.
public class StackOverflow_12608671
{
const string XML = #"<Pricing>
<Code>Success</Code>
<PricingQuotes>
<PricingQuote>
<ProductName>Conforming 30 Year Fixed</ProductName>
</PricingQuote>
<PricingQuote>
<ProductName>Conforming 20 Year Fixed</ProductName>
</PricingQuote>
</PricingQuotes>
</Pricing> ";
public class Pricing
{
public string Code { get; set; }
public List<PricingQuote> PricingQuotes { get; set; }
}
public class PricingQuote
{
public string ProductName { get; set; }
}
public static void Test()
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(XML));
XmlSerializer xs = new XmlSerializer(typeof(Pricing));
Pricing p = (Pricing)xs.Deserialize(ms);
foreach (var q in p.PricingQuotes)
{
Console.WriteLine(q.ProductName);
}
}
}

Related

Json.Net Deserializing list of c# objects throwing error

I have a list of objects in below json format. I would like to deserialize using below code. It is throwing unable to convert to object error. I have tried below three options, but didnt help. jsoninput is a IEnumerable<string>converted into json object using ToJson().
Error:
{"Error converting value \"{\"id\":\"11ef2c75-9a6d-4cef-8163-94daad4f8397\",\"name\":\"bracing\",\"lastName\":\"male\",\"profilePictureUrl\":null,\"smallUrl\":null,\"thumbnailUrl\":null,\"country\":null,\"isInvalid\":false,\"userType\":0,\"profilePrivacy\":1,\"chatPrivacy\":1,\"callPrivacy\":0}\" to type 'Api.Models.UserInfo'. Path '[0]', line 1, position 271."}
var requests1 = JsonConvert.DeserializeObject<UsersInfo>(jsoninput);
var requests2 = JsonConvert.DeserializeObject<IEnumerable<UserInfo>>(jsoninput);
var requests3 = JsonConvert.DeserializeObject<List<UserInfo>>(jsoninput);
//Below are my classes,
public class UsersInfo
{
public List<UserInfo> UserInfoList { get; set; }
public UsersInfo()
{
UserInfoList = new List<UserInfo>();
}
}
public class UserInfo
{
public string Id { set; get; }
public string Name { set; get; }
public string LastName { set; get; }
public string ProfilePictureUrl { set; get; }
public string SmallUrl { set; get; }
public string ThumbnailUrl { get; set; }
public string Country { set; get; }
public bool IsInvalid { set; get; }
}
Below is my json object,
["{\"id\":\"11ef2c75-9a6d-4cef-8163-94daad4f8397\",\"name\":\"bracing\",\"lastName\":\"male\",\"profilePictureUrl\":null,\"smallUrl\":null,\"thumbnailUrl\":null,\"country\":null,\"isInvalid\":false}","{\"id\":\"318c0885-2720-472c-ba9e-1d1e120bcf65\",\"name\":\"locomotives\",\"lastName\":\"riddles\",\"profilePictureUrl\":null,\"smallUrl\":null,\"thumbnailUrl\":null,\"country\":null,\"isInvalid\":false}"]
Looping through individual items in json input and if i deserialize it like below, it works fine. But i want to deserialize the list fully. Note: jsoninput was a IEnumerable<string> before i convert in json object.
foreach (var re in jsoninput)
{
var request0 = JsonConvert.DeserializeObject<UserInfo>(re);
}
Please look at this fiddle: https://dotnetfiddle.net/XpjuL4
This is the code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
//Below are my classes,
public class UsersInfo
{
public List<UserInfo> UserInfoList { get; set; }
public UsersInfo()
{
UserInfoList = new List<UserInfo>();
}
}
public class UserInfo
{
public string Id { set; get; }
public string Name { set; get; }
public string LastName { set; get; }
public string ProfilePictureUrl { set; get; }
public string SmallUrl { set; get; }
public string ThumbnailUrl { get; set; }
public string Country { set; get; }
public bool IsInvalid { set; get; }
}
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
Option1();
Option2();
}
public static void Option1(){
string json = #"{""UserInfoList"":[
{""id"":""11ef2c75 - 9a6d - 4cef - 8163 - 94daad4f8397"",""name"":""bracing"",""lastName"":""male"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false},
{ ""id"":""318c0885-2720-472c-ba9e-1d1e120bcf65"",""name"":""locomotives"",""lastName"":""riddles"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false}
]}";
var obj = JsonConvert.DeserializeObject<UsersInfo>(json);
obj.UserInfoList.ForEach(e => Console.WriteLine(e.Id));
}
public static void Option2(){
string json = #"[
{""id"":""11ef2c75 - 9a6d - 4cef - 8163 - 94daad4f8397"",""name"":""bracing"",""lastName"":""male"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false},
{ ""id"":""318c0885-2720-472c-ba9e-1d1e120bcf65"",""name"":""locomotives"",""lastName"":""riddles"",""profilePictureUrl"":null,""smallUrl"":null,""thumbnailUrl"":null,""country"":null,""isInvalid"":false}
]";
var obj = JsonConvert.DeserializeObject<List<UserInfo>>(json);
obj.ForEach(e => Console.WriteLine(e.Id));
}
}
Both work, and are basically very close to what you are doing. You can either serialize it as a list (based on your json, I think that's the closest to your use case, and that's Option 2).
However, put extra attention to the JSON. I had to re-parse your JSON to make it work (https://jsonformatter.org/json-parser is a nice website to do it). For the sake of explaining the example, in C#, # means raw string, and in raw string, quotes are escaped with double quotes "".
I would expect that the business logic generating this JSON is not correct, if the JSON you pasted is the direct result from it.
EDIT
Given the OP's comment:
Thanks Tu.ma for your thoughts. The other method returns
IEnumerable which is nothing but
Dictionary.Where(x => x.Value == null).Select(x =>
x.Key).ToHashSet(). The values in Dictionary are -> Key
is String, Value is UserInfo object serialized. So, in that case i
should deserialize one by one? If not, i should serialize entire list
in one shot? Am i right? – Raj 12 hours ago
The problem is in the way you are generating the list of UsersInfo. The result from Dictionary<string,string>.Where(x => x.Value == null).Select(x =>
x.Key).ToHashSet() is a bunch of strings, not of objects, so you need to serialize them one by one.
If you are worried about the linearity of the approach, you could consider running through it in parallel. Of course, you need to judge if it fits your application.
var userInfoStrings = Dictionary<string,string>.Where(x => x.Value == null).Select(x => x.Key).ToHashSet();
var UserInfoList = userInfoStrings.AsParallel().Select (u => JsonConvert.DeserializeObject<UsersInfo>(u)).ToList();

Delete nodes from returned from response.content using deserialization in RestSharp

I know the Response.Content from my GET RestRequest is xml UTF-8. However I want to delete unwanted child nodes from the response.content. I can't assign the response to an xml document (because it returns a string apparently ?) which I could use system.xml to delete unwanted child nodes.
I have a situation (below) where if I have three space_reservation nodes [1401, 1402, and 1401 & 1402], I want to delete the space_reservation nodes for MH-1402 and (MH1401 & MH-1402), and keep the space_reservation node for MH-1401. I want to do this before I use the a REST API to schedule the rooms because these room will present a duplicate schedule situation.
Here is the example of the response.content from the request:
<?xml version="1.0" encoding="UTF-8"?>
<r25:reservations xmls:xsi="http://www.w3.org/2001/XMLSchemainstance">
<r25:reservation xl:href="reservation.xml?rsrv_id=731397">
<r25:reservation_id>7313</r25:reservation_id>
<r25:reservation_state>1</r25:reservation_state>
<r25:event_start_dt>2016-04-12T09:00:00-07:00</r25:event_start_dt>
<r25:event_end_dt>2016-04-12T12:00:00-07:00</r25:event_end_dt>
<r25:event_id xl:href="event.xml?event_id=197559">197559</r25:event_id>
<r25:event_locator>2016-ABAHZP</r25:event_locator>
<r25:event_name>Spring Grand Rounds</r25:event_name>
<r25:event_type_name>Department Meetings & Events</r25:event_type_name>
<r25:organization_name>Sciences</r25:organization_name>
<r25:profile_name>April 12th Capture</r25:profile_name>
<r25:space_reservation xl:href="space.xml?space_id=335">
<r25:space_name>MH-1401</r25:space_name>
<r25:space_id>335</r25:space_id>
<r25:space_instruction_id>94367</r25:space_instruction_id>
</r25:space_reservation>
<r25:space_reservation xl:href="space.xml?space_id=336">
<r25:space_name>MH-1402</r25:space_name>
<r25:space_id>336</r25:space_id>
<r25:space_instruction_id>94368</r25:space_instruction_id>
</r25:space_reservation>
<r25:space_reservation xl:href="space.xml?space_id=337">
<r25:space_name>MH-1401 & 1402</r25:space_name>
<r25:space_id>337</r25:space_id>
<r25:space_instruction_id>94366</r25:space_instruction_id>
</r25:space_reservation>
<r25:resource_reservation xl:href="resource.xml?resource_id=55">
<r25:resource_id>55</r25:resource_id>
<r25:resource_name>Live plus on-demand</r25:resource_name>
<r25:resource_count>1</r25:resource_count>
<r25:resource_instruction_id/>
<r25:resource_instructions/>
</r25:resource_reservation>
</r25:reservation>
</r25:reservations>
Here are my deserialization classes:
public class Mreservation : List<reservation> { }
public class reservation
{
public string event_name { get; set; }
public DateTime reservation_start_dt { get; set; }
public DateTime reservation_end_dt { get; set; }
public DateTime event_start_dt { get; set; }
public DateTime event_end_dt { get; set; }
public string event_locator { get; set; }
public int organization_id { get; set; }
public List<space_reservation> spaceNodes { get; set; }
public List<resource_reservation> resourceNodes { get; set; }
}
public class reservations
{
public string pubdate { get; set; }
public List<reservation> ReservationNodes { get; set; }
}
public class space_reservation
{
public string space_name { get; set; }
public int space_id { get; set; }
}
public class resource_reservation
{
public int resource_id { get; set; }
public string resource_name { get; set; }
}
Here is the code I have for eliminating nodes that have title 1401. I save to xml so I can confirm that the node has been deleted but still the same.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RestSharp;
using System.Xml;
namespace EliminateDuplicates
{
class Program
{
static void Main(string[] args)
{
var R25Client = R25_Rest_Login.R25Login();
String testDate = new DateTime(2016, 4, 12).ToString("yyyyMMdd");
var CaptureRequest = new RestRequest("reservations.xml", Method.GET);
CaptureRequest.AddParameter("resource_query_id", "35304");
CaptureRequest.AddParameter("start_dt", testDate);
CaptureRequest.AddParameter("end_dt", testDate);
CaptureRequest.RequestFormat = DataFormat.Xml;
var CaptureResponse = R25Client.Execute<Mreservation>(CaptureRequest);
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(CaptureResponse.Content);
xdoc.Save("beforeRemoval.xml");
foreach (var x in CaptureResponse.Data)
{
if ((x.spaceNodes[0].space_id == 335) && (x.spaceNodes[1].space_id == 336) && (x.spaceNodes[2].space_id == 337))
{
x.spaceNodes.RemoveAll(i => i.space_id == 335);
}
}
XmlDocument xdocA = new XmlDocument();
xdocA.LoadXml(CaptureResponse.Content);
xdocA.Save("afterRemoval.xml");
}
}
}
Im looking for the proper way to delete these nodes using RestSharp ?
The proper way of transforming structure of responses in RestSharp would be implementing a custom IDeserializer.
However, it seems you want to apply business logic (i.e. remove some reservations) instead of just deserializing. In that case, you should use the already-deserialized object as shown in the example below.
var reservations = client.Execute<Mreservation>(request).Data;
foreach(var reservation in reservations)
{
reservation.SpaceNodes.RemoveAll((space) => someCondition(space));
}
// ...go on using reservations

Deserialize JSON string into a list for dropdownlist in C#

I have a windows form application and would like to deserialize a JSON string that I'm getting from a web address so that I can get just two values from it, how would I go about doing this?
Below is the code I have to get the JSON string, and if you go to the URL that it's getting, you can also see the JSON string. I want to just get the item name, and current price of it. Which you can see the price under the current key.
private void GrabPrices()
{
using (WebClient webClient = new System.Net.WebClient())
{
WebClient n = new WebClient();
var json = n.DownloadString("http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=1513");
string valueOriginal = Convert.ToString(json);
Console.WriteLine(json);
}
}
It's also going to be iterating through a SQLite database and getting the same data for multiple items based on the item ID, which I'll be able to do myself.
EDIT I'd like to use JSON.Net if possible, I've been trying to use it and it seems easy enough, but I'm still having trouble.
Okay so first of all you need to know your JSON structure, sample:
[{
name: "Micheal",
age: 20
},
{
name: "Bob",
age: 24
}]
With this information you can derive a C# object
public class Person
{
public string Name {get;set;}
public int Age {get;set;}
}
Now you can use JSON.NET to deserialize your JSON into C#:
var people = JsonConvert.DeserializeObject<List<Person>>(jsonString);
If you look at the original JSON it is an array of objects, to deal with this I have used List<T>.
Key things to remember, you need to have the C# object mirror in properties that of the JSON object. If you don't have a list, then you don't need List<T>.
If your JSON objects have camel casing, and you want this converted to the C# conventions, then use this:
var people = JsonConvert.DeserializeObject<List<Person>>(
jsonString,
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
First of all you need to create a class structure for the JSON
public class Wrapper
{
public Item item;
}
public class Item
{
public string icon { get; set; }
public string icon_large { get; set; }
public int id { get; set; }
public string type { get; set; }
public string typeIcon { get; set; }
public string name { get; set; }
public string description { get; set; }
public GrandExchange current { get; set; }
public GrandExchange today { get; set; }
public bool members { get; set; }
public GrandExchange day30 { get; set; }
public GrandExchange day90 { get; set; }
public GrandExchange day180 { get; set; }
}
public class GrandExchange
{
public string trend { get; set; }
public string price { get; set; }
}
Then you need to serialize the current item into a Wrapper class
var wrapper = JsonConvert.DeserializeObject<Wrapper>(json);
Then if you want multiple items in a list, you can do so with this code :
// Items to find
int[] itemIds = {1513, 1514, 1515, 1516, 1517};
// Create blank list
List<Item> items = new List<Item>();
foreach (int id in itemIds)
{
var n = new WebClient();
// Get JSON
var json = n.DownloadString(String.Format("http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item={0}", id));
// Parse to Item object
var wrapper = JsonConvert.DeserializeObject<Wrapper>(json);
// Append to list
items.Add(wrapper.item);
}
// Do something with list
It is also worth noting that Jagex limit how many times this API can be called from a certain IP within a time frame, going over that limit will block your IP for a certain amount of time. (Will try and find a reference for this)

How to load XML Elements using LINQ from XDocument into a class (not using Descendants)

How to load XML Elements using LINQ from XDocument into a c# class. I don't want to use XDocument.Descendants because the settings are not repeated and occur only once in the XML.
This works but I think there must be another way where I don't have to use IEnumerable or use ToArray() and use the first element [0]. Any suggestions?
CODE
public class BackupItem // class needed so LINQ reads XML into an editable DbGrid
{
public bool Backup { get; set; }
public bool IncludeSubDir { get; set; }
public string BackupLabel { get; set; }
public string BackupPath { get; set; }
}
public class BackupSettings
{
public bool IncludeDateStamp { get; set; }
public bool DatePrefix { get; set; }
public bool DateSuffix { get; set; }
public string DateFormat { get; set; }
public bool ZipCompress { get; set; }
public bool ZipPrefix { get; set; }
public bool ZipSuffix { get; set; }
public string ZipText { get; set; }
public bool BackupToSiblingFolder { get; set; }
public string SiblingFolder { get; set; }
public bool BackupToFolder { get; set; }
public bool IncludeFullPath { get; set; }
public string BackupFolder { get; set; }
}
// use a LINQ query to load xml file into datagridview and make datagrid editable (create class with get/set)
var q = from arg in GvXMLDoc.Descendants("BackupItem")
select new BackupItem()
{
Backup = (bool)arg.Element("IncludeDirectory"),
IncludeSubDir = (bool)arg.Element("IncludeSubDirectories"),
BackupLabel = (string)arg.Element("BackupLabel"),
BackupPath = (string)arg.Element("BackupPath")
};
dataGridView1.DataSource = q.ToList();
// load global variable
IEnumerable<BackupSettings> GvBackupSettings = from arg in GvXMLDoc.Element("Backup").Elements("Settings")
select new BackupSettings()
{
IncludeDateStamp = (bool)arg.Element("IncludeDateStamp"),
DatePrefix = (bool)arg.Element("DatePrefix"),
DateSuffix = (bool)arg.Element("DateSuffix"),
DateFormat = (string)arg.Element("DateFormat"),
ZipCompress = (bool)arg.Element("ZipCompress"),
ZipPrefix = (bool)arg.Element("ZipPrefix"),
ZipSuffix = (bool)arg.Element("ZipSuffix"),
ZipText = (string)arg.Element("ZipText"),
BackupToSiblingFolder = (bool)arg.Element("BackupToSiblingFolder"),
SiblingFolder = (string)arg.Element("SiblingFolder"),
BackupToFolder = (bool)arg.Element("BackupToFolder"),
IncludeFullPath = (bool)arg.Element("IncludeFullPath"),
BackupFolder = (string)arg.Element("BackupFolder")
};
I can access the values but it seems a very 'messy way'.
var s = GvBackupSettings.ToArray()[0].DateSuffix;
var t = GvBackupSettings.ToArray()[0].DateFormat;
XML FILE
<?xml version='1.0'?>
<Backup>
<Settings>
<IncludeDateStamp>true</IncludeDateStamp>
<DatePrefix>false</DatePrefix>
<DateSuffix>true</DateSuffix>
<DateFormat>yyyy-MM-dd h.m.s</DateFormat>
<ZipCompress>false</ZipCompress>
<ZipPrefix>false</ZipPrefix>
<ZipSuffix>false</ZipSuffix>
<ZipText></ZipText>
<BackupToSiblingFolder>true</BackupToSiblingFolder>
<SiblingFolder>backups</SiblingFolder>
<BackupToFolder>false</BackupToFolder>
<IncludeFullPath>true</IncludeFullPath>
<BackupFolder>C:\\backup</BackupFolder>
</Settings>
<BackupItem>
<IncludeDirectory>true</IncludeDirectory>
<IncludeSubDirectories>true</IncludeSubDirectories>
<BackupLabel>Backup1</BackupLabel>
<BackupPath>C:\TestFiles\Xml\Samples</BackupPath>
</BackupItem>
<BackupItem>
<IncludeDirectory>true</IncludeDirectory>
<IncludeSubDirectories>false</IncludeSubDirectories>
<BackupLabel>Backup2</BackupLabel>
<BackupPath>C:\TestFiles\Xml\Samples</BackupPath>
</BackupItem>
</Backup>
EDIT 1
I also am trying to deserialize the XML (thanks for idea) so I don't have to cast each item. This does not work.. I don't want to have to run the XSD tool either and have a huge class file...
XmlRootAttribute rootAttribute = new XmlRootAttribute("Backup");
XmlSerializer deserializer = new XmlSerializer((typeof(BackupSettings)), rootAttribute);
GvBackupSettings = (BackupSettings)deserializer.Deserialize(XmlReader.Create(GvXMLFileName));
EDIT 2
Ended up serializing and deserializing xml as suggested below using stardard XSD.exe tool to generate the c# class. Especially as I had to read and write to same xml file. Note: Make sure you verify/modify the generated xsd file first.
You don't need a query if you just need the first element:
XElement settings = GvXMLDoc.Element("Backup").Element("Settings");
BackupSettings GvBackupSettings = new BackupSettings
{
IncludeDateStamp = (bool)settings.Element("IncludeDateStamp"),
DatePrefix = (bool)settings.Element("DatePrefix"),
...
};
var s = GvBackupSettings.DateSuffix;
var t = GvBackupSettings.DateFormat;

LINQ XML Read different hierarchies into 1 object

I have an XML file
<searchResponse requestID=“500” status=“success”>
<pso>
<psoID ID=“770e8400-e29b-41d4-a716-446655448549”
targetID=“mezeoAccount”/>
<data>
<email>user2#example.net</email>
<quotaMeg>100</quotaMeg>
<quotaUsed>23</quotaUsed>
<realm>Mezeo</realm>
<path>/san1/</path>
<billing>user2</billing>
<active>true</active>
<unlocked>true</unlocked>
<allowPublic>true</allowPublic>
<bandwidthQuota>1000000000</bandwidthQuota>
<billingDay>1</billingDay>
</data>
</pso>
</searchRequest>
and I want to extract the data into a single business object. Am I better to go
MezeoAccount mcspAccount = new MezeoAccount();
mcspAccount.PsoID = doc.Element("psoID").Attribute("ID").Value;
mcspAccount.Email = doc.Element("email").Value;
...
or build a list even though I know there is only 1 record in the file?
var psoQuery = from pso in doc.Descendants("data")
select new MezeoAccount {
PsoID = pso.Parent.Element("psoID").Attribute("ID").Value,
Email = pso.Element("email").Value,
... };
What would people suggest would be the more correct way, or a better way even, if I missed something.
If you know that your xml only will contain one record of the data in mind you shouldn't create a list for it. So your first example looks fine.
A pattern I personally use is something like this:
public class MezeoAccount
{
public string PsoID { get; set; }
public string Email { get; set; }
public static MezeoAccount CreateFromXml(XmlDocument xml)
{
return new MezeoAccount()
{
PsoID = xml.Element("psoID").Attribute("ID").Value,
Email = doc.Element("email").Value;
};
}
}
//Usage
var mezeoAccount = MezeoAccount.CreateFromXml(xml);
It looks like you didn't get a working answer to this question. Assuming that there can only be one account in the XML file, I would do it like this:
using System;
using System.Linq;
using System.Xml.Linq;
public class MezeoAccount
{
public string PsoId { get; set; }
public string Email { get; set; }
public int QuotaMeg { get; set; }
// Other properties...
}
public class Program
{
public static void Main()
{
XDocument doc = XDocument.Load("input.xml");
XElement pso = doc.Element("searchResponse").Element("pso");
XElement data = pso.Element("data");
MezeoAccount x = new MezeoAccount
{
PsoId = pso.Element("psoID").Attribute("ID").Value,
Email = data.Element("email").Value,
QuotaMeg = int.Parse(data.Element("quotaMeg").Value),
// Other properties...
};
}
}

Categories