Problems deserializing List of objects - c#

I am having trouble deserializing a list of objects. I can get just one object to serialize into an object but cannot get the list. I get no error it just returns an empty List. This is the XML that gets returned:
<locations>
<location locationtype="building" locationtypeid="1">
<id>1</id>
<name>Building Name</name>
<description>Description of Building</description>
</location>
</locations>
This is the class I have and I am deserializing in the GetAll method:
[Serializable()]
[XmlRoot("location")]
public class Building
{
private string method;
[XmlElement("id")]
public int LocationID { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("mubuildingid")]
public string MUBuildingID { get; set; }
public List<Building> GetAll()
{
var listBuildings = new List<Building>();
var building = new Building();
var request = WebRequest.Create(method) as HttpWebRequest;
var response = request.GetResponse() as HttpWebResponse;
var streamReader = new StreamReader(response.GetResponseStream());
TextReader reader = streamReader;
var serializer = new XmlSerializer(typeof(List<Building>),
new XmlRootAttribute() { ElementName = "locations" });
listBuildings = (List<Building>)serializer.Deserialize(reader);
return listBuildings;
}
}

Try this:
[XmlRoot("locations")]
public class BuildingList
{
public BuildingList() {Items = new List<Building>();}
[XmlElement("location")]
public List<Building> Items {get;set;}
}
Then deserialize the whole BuildingList object.
var xmlSerializer = new XmlSerializer(typeof(BuildingList));
var list = (BuildingList)xmlSerializer.Deserialize(xml);

I know this is an old(er) question, but I struggled with this today, and found an answer that doesn't require encapsulation.
Assumption 1: You have control over the source Xml and how it is constructed.
Assumption 2: You are trying to serialise the Xml directly into a List<T> object
You must name the Root element in the Xml as ArrayOfxxx where xxx is the name of your class (or the name specified in XmlType (see 2.))
If you wish your xml Elements to have a different name to the class, you should use XmlType on the class.
NB: If your Type name (or class name) starts with a lowercase letter, you should convert the first character to uppercase.
Example 1 - Without XmlType
class Program
{
static void Main(string[] args)
{
//String containing the xml array of items.
string xml =
#"<ArrayOfItem>
<Item>
<Name>John Doe</Name>
</Item>
<Item>
<Name>Martha Stewart</Name>
</Item>
</ArrayOfItem>";
List<Item> items = null;
using (var mem = new MemoryStream(Encoding.Default.GetBytes(xml)))
using (var stream = new StreamReader(mem))
{
var ser = new XmlSerializer(typeof(List<Item>)); //Deserialising to List<Item>
items = (List<Item>)ser.Deserialize(stream);
}
if (items != null)
{
items.ForEach(I => Console.WriteLine(I.Name));
}
else
Console.WriteLine("No Items Deserialised");
}
}
public class Item
{
public string Name { get; set; }
}
Example 2 - With XmlType
class Program
{
static void Main(string[] args)
{
//String containing the xml array of items.
//Note the Array Name, and the Title case on stq.
string xml =
#"<ArrayOfStq>
<stq>
<Name>John Doe</Name>
</stq>
<stq>
<Name>Martha Stewart</Name>
</stq>
</ArrayOfStq>";
List<Item> items = null;
using (var mem = new MemoryStream(Encoding.Default.GetBytes(xml)))
using (var stream = new StreamReader(mem))
{
var ser = new XmlSerializer(typeof(List<Item>)); //Deserialising to List<Item>
items = (List<Item>)ser.Deserialize(stream);
}
if (items != null)
{
items.ForEach(I => Console.WriteLine(I.Name));
}
else
Console.WriteLine("No Items Deserialised");
}
}
[XmlType("stq")]
public class Item
{
public string Name { get; set; }
}

Not sure how Building corresponds with the location you have in your xml, but to me it makes more sense if they're named equivalently. Instead of using a List use a LocationList, and it becomes:
[Serializable()]
[XmlRoot("locations")]
public class LocationCollection{
[XmlElement("location")]
public Location[] Locations {get;set;}
}
[Serializable()]
[XmlRoot("location")]
public class Location
{
[XmlElement("id")]
public int LocationID { get; set; }
[XmlAttribute("locationtype")]
public string LocationType {get;set;}
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("mubuildingid")]
public string MUBuildingID { get; set; }
}
You can then deserialize as follows:
var request = WebRequest.Create(method) as HttpWebRequest;
var response = request.GetResponse() as HttpWebResponse;
var streamReader = new StreamReader(response.GetResponseStream());
TextReader reader = streamReader;
var serializer = new XmlSerializer(typeof(LocationCollection),
new XmlRootAttribute() { ElementName = "locations" });
var listBuildings = (LocationCollection)serializer.Deserialize(reader);
return listBuildings;

I know, old question, but came across it when faced by a similar issue.
Building on #ricovox's answer and in context of the OP's question, this is the model I would use to serialize his xml:
[Serializable, XmlRoot("locations")]
public class BuildingList
{
[XmlArrayItem("location", typeof(Building))]
public List<Building> locations { get; set; }
}
[Serializable]
public class Building
{
public int LocationID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string MUBuildingID { get; set; }
public List<Building> GetAll()
{
...
}
}
The OP's mistake was to create the list item as the root

Use [XMLArray] for collection properties.

Related

How can I parse some JSON dynamically without knowing JSON values?

So I am using TDAmeritrade API to receive stock data with a C# Winforms program on Visual Studio. It takes the user input stock symbol and searches for the info. I am using HttpClient and Newtonsoft.Json and have been able to successfully perform the GET request and receive a JSON string back, but I do not know how to get all of the information I need out of it.
Here is the JSON:
https://drive.google.com/file/d/1TpAUwjyqrHArEXGXMof_K1eQe0hFoaw5/view?usp=sharing
Above is the JSON string sent back to me then formatted. My goal is to record information for each price in "callExpDateMap.2021-02-19:11" and "callExpDateMap.2021-03-19:39". The problem is that for each different stock, the dates that show up in "callExpDateMap" are going to be different.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync(url);
var info = await response.Content.ReadAsStringAsync();
dynamic config = JsonConvert.DeserializeObject<dynamic>(info, new ExpandoObjectConverter());
return config;
This is the code I have right now. I know the last for statement is not correct. How can I parse to the specific sections I want (callExpDateMap.expirationdate.StrikePrice) and get the information needed from each without knowing the dates and Strike prices beforehand? Is there a way to innumerate it and search through the JSON as if it were all a bunch of arrays?
The code below is perhaps not the most elegant nor complete, but I think it will get you going. I would start by using the JObject.Parse() from the Newtonsoft.Json.Linq namespace and take it from there.
JObject root = JObject.Parse(info);
string symbol = root["symbol"].ToObject<string>();
foreach (JToken toplevel in root["callExpDateMap"].Children())
{
foreach (JToken nextlevel in toplevel.Children())
{
foreach (JToken bottomlevel in nextlevel.Children())
{
foreach (JToken jToken in bottomlevel.Children())
{
JArray jArray = jToken as JArray;
foreach (var arrayElement in jArray)
{
InfoObject infoObject = arrayElement.ToObject<InfoObject>();
Console.WriteLine(infoObject.putCall);
Console.WriteLine(infoObject.exchangeName);
Console.WriteLine(infoObject.multiplier);
}
}
}
}
}
public class InfoObject
{
public string putCall { get; set; }
public string symbol { get; set; }
public string description { get; set; }
public string exchangeName { get; set; }
// ...
public int multiplier { get; set; }
// ...
}
This is official documentation of Newtonsoft method you are trying to use.
https://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
If an API's method returns different json propeties and you cannot trust it's property names all the times, then you can try using a deserialize method that returns .Net object, for example: JsonConvert.DeserializeObject Method (String)
https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
That method's signature is this:
public static Object DeserializeObject(string value)
Parameter is: value of type json string.
Return Value is: Object of type object.
If you do not want an Object, then you can of course use a .Net type you have. Such as this method:
JsonConvert.DeserializeObject Method (String)
Any property that you have in both (the .net type and json object) will get populated. If .net type has properties that do not exist in json object, then those will be ignored. If json object has properties that do not exist in.net, then those will be ignored too.
Here's an example of a .Net type
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MyNameSpace
{
public class TDAmeritradeStockData
{
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("callExpDateMap")]
public object CallExpDateMap { get; set; }
//...
//...
public CallExpDateMapType[] CallExpDateMapList { get; set; }
}
public class CallExpDateMapType
{
[JsonProperty("expirationdate")]
public string Expirationdate { get; set; }
[JsonProperty("StrikePrice")]
public List<StrikePriceType> StrikePriceList { get; set; }
}
public class StrikePriceType
{
public string StrikePrice { get; set; }
public List<StrikePricePropertiesType> StrikePricePropertiesList { get; set; }
}
public class StrikePricePropertiesType
{
[JsonProperty("putCall")]
public string PutCall { get; set; }
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("exchangeName")]
public string ExchangeName { get; set; }
[JsonProperty("bid")]
public double Bid { get; set; }
[JsonProperty("ask")]
public double Ask { get; set; }
//...
//...
}
[TestClass]
public class TestTestTest
{
[TestMethod]
public void JsonTest()
{
var jsondata = ReadFile("data.json");
var model = JsonConvert.DeserializeObject<TDAmeritradeStockData>(jsondata);
JObject jObject = (JObject)model.CallExpDateMap;
var count = ((JObject)model.CallExpDateMap).Count;
model.CallExpDateMapList = new CallExpDateMapType[count];
var jToken = (JToken)jObject.First;
for (var i = 0; i < count; i++)
{
model.CallExpDateMapList[i] = new CallExpDateMapType
{
Expirationdate = jToken.Path,
StrikePriceList = new List<StrikePriceType>()
};
var nextStrikePrice = jToken.First.First;
while (nextStrikePrice != null)
{
var nextStrikePriceProperties = nextStrikePrice;
var srikePriceList = new StrikePriceType
{
StrikePrice = nextStrikePriceProperties.Path,
StrikePricePropertiesList = JsonConvert.DeserializeObject<List<StrikePricePropertiesType>>(nextStrikePrice.First.ToString())
};
model.CallExpDateMapList[i].StrikePriceList.Add(srikePriceList);
nextStrikePrice = nextStrikePrice.Next;
}
jToken = jToken.Next;
}
Assert.IsNotNull(model);
}
private string ReadFile(string fileName)
{
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var data = new StringBuilder();
using (var streamReader = new StreamReader(fileStream))
{
while (!streamReader.EndOfStream) data.Append(streamReader.ReadLine());
streamReader.Close();
}
fileStream.Close();
return data.ToString();
}
}
}
}

Parse JSON String into List

It is needed to parse JSONString into List. (List of instances)
I'm trying to use JSON.NET by Newtonsoft.
I have classes:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Manufactorers { get; set; }
}
The JSON string looks something like this:
[
{
"Column0":23.0,
"Column1":"Евроен",
"Column2":"https://www.123.com",
"Column3":"Фак"
},
{
"Column0":24.0,
"Column1":"Еил",
"Column2":"https://www.123.com",
"Column3":"Старт"
}
]
I've been trying to do something like this:
string JSONString = string.Empty;
JSONString = JsonConvert.SerializeObject(result);
List<Item> items = JsonConvert.DeserializeObject<List<Item>>(JSONString);
But it returns 0 and null.
I have no idea, how to fix it.
Also here I truy to parse Excel file. This code works, but after deserialization, I have just 0 and null.
var filePath = #"..\..\..\..\doc.xlsx";
using (var steam = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(steam))
{
var result = reader.AsDataSet().Tables["Лист1"];
string JSONString = string.Empty;
JSONString = JsonConvert.SerializeObject(result);
List<Item> items = JsonConvert.DeserializeObject<List<Item>>(JSONString);
}
}
The naming of JSON and your class does not match. This can be fixed using JsonProperty attributes:
[JsonProperty("Column0")]
public decimal ID { get; set; }
Second, JSON deserizlizer can not deserialize string "23.0" to int when there is decimal point. You can retype ID to decimal or double to make it work.
Little test here:
public class TestClass
{
[JsonProperty("Column0")]
public decimal ID { get; set; }
}
Then the deserialization works without errors:
var testClassJson = "{\"Column0\": 12.0}";
var i = JsonConvert.DeserializeObject<TestClass>(testClassJson);

XML - Get Element Value - C#

I am trying to find if an element value exists then get a element value below it. i.e.
<Reply>
<customer id="1223">
<group>A</group>
<class>
<custclass>AB</custclass>
<custval>1</custval>
</class>
<class>
<custclass>CD</custclass>
<custval>2</custval>
</class>
</customer>
</Reply>
I need to get the custval element value if the custclass = "CD". What is the best way to set this into a string in C# "2"? So I am looking for if custclass element value of "CD" exists then return the custval element value of 2.
Thanks for any info.
We can read the property value using various ways-
Method 1 - using XmlDocument
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList xnl = doc.SelectNodes("/Reply/customer/class");
foreach (XmlNode node in xnl)
{
if (node.ChildNodes[0].InnerText == "CD")
{
Console.WriteLine(node.ChildNodes[1].InnerText);
}
}
Method 2 - using XDcoument and LINQ
XDocument xml = XDocument.Load(xmlFile);
var result = xml.Root.DescendantsAndSelf("class")
.Where(r => (string)r.Element("custclass").Value == "CD")
.Select(s=> (string)s.Element("custval").Value).Single();
Console.WriteLine(result);
Method 3 - using XDocument and XPathSelectElement
var custval = xml.XPathSelectElement("Reply/customer/class[custclass='CD']/custval").Value;
Console.WriteLine(custval);
Method 4 - using XmlSerializer
Create C# classes using xmltocsharp & use Deserialize to convert the xml to object
[XmlRoot(ElementName = "class")]
public class Class
{
[XmlElement(ElementName = "custclass")]
public string Custclass { get; set; }
[XmlElement(ElementName = "custval")]
public string Custval { get; set; }
}
[XmlRoot(ElementName = "customer")]
public class Customer
{
[XmlElement(ElementName = "group")]
public string Group { get; set; }
[XmlElement(ElementName = "class")]
public List<Class> Class { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "Reply")]
public class Reply
{
[XmlElement(ElementName = "customer")]
public Customer Customer { get; set; }
}
static async System.Threading.Tasks.Task Main(string[] args)
{
string xmlFile = #"xxxxxx.xml";
using (StreamReader r = new StreamReader(xmlFile))
{
string xmlString = r.ReadToEnd();
XmlSerializer ser = new XmlSerializer(typeof(Reply));
using (TextReader reader = new StringReader(xmlString))
{
var result = (Reply)ser.Deserialize(reader);
var custvalue = result.Customer.Class.Where(i => i.Custclass == "CD").Select(a => a.Custval).Single();
Console.WriteLine(custvalue);
}
}
}

Add schemaLocation to XML serializing List<T> using XmlSerializer

I'm trying to add schemaLocation attribute to XML root element when serializing List<T>. Code works fine if I'm serializing just one object but does not work on lists. My current code:
public class SomeObject
{
public int Id { get; set; }
public string Name { get; set; }
}
public class XmlListContainer<T> : List<T>
{
[XmlAttribute(Namespace = XmlSchema.InstanceNamespace)]
public string schemaLocation = "http :// localhost/someschema";
}
public class BuildXml
{
public static void GetXml()
{
var list = new XmlListContainer<SomeObject>()
{
new SomeObject() { Id = 1, Name = "One" },
new SomeObject() { Id = 2, Name = "Two" },
};
var objectToXml = list;
string output;
using (var writer = new StringWriter())
{
var xs = new XmlSerializer(objectToXml.GetType());
var nameSpaces = new XmlSerializerNamespaces();
nameSpaces.Add("xsi", "http :// www.w3.org/2001/XMLSchema-instance");
xs.Serialize(writer, objectToXml, nameSpaces);
output = writer.GetStringBuilder().ToString();
writer.Close();
}
Console.WriteLine(output);
}
}
XML root element appears without schemaLocation:
<ArrayOfSomeObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
If I'm changing code to
public class SomeObject
{
public int Id { get; set; }
public string Name { get; set; }
[XmlAttribute(Namespace = XmlSchema.InstanceNamespace)]
public string schemaLocation = "http :// localhost/someschema";
}
...
var objectToXml = new SomeObject() { Id = 1, Name = "One" };
...all looks fine but I need list list
<SingleObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://localhost/someschema">
Is it possible to add schemaLocation attribute when serializing List<T>?

parse xml children nodes

What is the best way to parse XML children nodes into a specific list? This is a small example of the XML.
<Area Name="Grey Bathroom" IntegrationID="3" OccupancyGroupAssignedToID="141">
<Outputs>
<Output Name="Light/Exhaust Fan" IntegrationID="46" OutputType="NON_DIM" Wattage="0" />
</Outputs>
</Area>
I want to create a list or something that will be called the Area Name and hold the information of the Output Name and IntegrationID. So I can call the list and pull out the Output Name and IntegrationID.
I can create a list of all Area Names and then a list of Outputs but cannot figure out how to create a list that will be called "Grey Bathroom" and hold the output "Light/Exhaust Fan" with an ID of 46.
XDocument doc = XDocument.Load(#"E:\a\b.xml");
List<Area> result = new List<Area>();
foreach (var item in doc.Elements("Area"))
{
var tmp = new Area();
tmp.Name = item.Attribute("Name").Value;
tmp.IntegrationID = int.Parse(item.Attribute("IntegrationID").Value);
tmp.OccupancyGroupAssignedToID = int.Parse(item.Attribute("OccupancyGroupAssignedToID").Value);
foreach (var bitem in item.Elements("Outputs"))
{
foreach (var citem in bitem.Elements("Output"))
{
tmp.Outputs.Add(new Output
{
IntegrationID = int.Parse(citem.Attribute("IntegrationID").Value),
Name = citem.Attribute("Name").Value,
OutputType = citem.Attribute("OutputType").Value,
Wattage = int.Parse(citem.Attribute("Wattage").Value)
});
}
}
result.Add(tmp);
}
public class Area
{
public String Name { get; set; }
public int IntegrationID { get; set; }
public int OccupancyGroupAssignedToID { get; set; }
public List<Output> Outputs = new List<Output>();
}
public class Output
{
public String Name { get; set; }
public int IntegrationID { get; set; }
public String OutputType { get; set; }
public int Wattage { get; set; }
}
The example uses an anonymous type. You could (and I warmly advice you to) use your own.
var doc = XDocument.Parse(xml);
var areaLists = doc.Elements("Area").
Select(e => e.Descendants("Output").
Select(d => new
{
Name = (string) d.Attribute("Name"),
Id = (int) d.Attribute("IntegrationID")
}).
ToArray()).
ToList();

Categories