XML Parsing to c# objects - c#

I am trying to do a XML parser which will extract data from a website using REST service, the protocol for communication is HTTP, the data I get is in XML format, and I get to the data I need after several requests to different addresses on the server. I need to parse this data to c# objects so I can operate with them lately.
The information on the server is on 5 levels( I am willing to make work only 4 of them for know)
1-List of vendors
2-List of groups
3-List of subgroups
4-List of products
5-List of full information about products
After I get to 4th level I need to check if the product is in my database or if it has different details so I can add or update it.
With "GET" request to a server I get XML with this structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vendors>
<vendor>
<id>someID</id>
<name>someName</name>
</vendor>
<vendor>
<id>someId1</id>
<name>somename1</name>
</vendor>
</vendors>
XML structure for groups is the same :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<groups vendor_id="43153185318">
<group>
<id>someID</id>
<name>someName</name>
</group>
<group>
<id>someId1</id>
<name>somename1</name>
</group>
The XML structure is analogical for subgroups and products, except that for products I have more elements like catalog_num, price etc.
I made the classes as follows :
public class VendorList
{
public List<Vendor> vendor_list { get; set; }
public VendorList()
{
vendor_list = new List<Vendor>();
}
}
public class Vendor
{
public string id { get; set; }
public string name { get; set; }
public List<Group> groups_list { get; set; }
public Vendor()
{
id = "N/A";
name = "N/A";
groups_list = new List<Group>();
}
}
public class Group
{
public string id { get; set; }
public string name { get; set; }
public List<SubGroup> subgroup_list { get; set; }
public Group()
{
id = "N/A";
name = "N/A";
subgroup_list = new List<SubGroup>();
}
}
public class SubGroup
{
public string id { get; set; }
public string name { get; set; }
public List<Product> product_list { get; set; }
public SubGroup()
{
id = "N/A";
name = "N/A";
product_list = new List<Product>();
}
}
public class Product
{
public string available { get; set; }
public string catalog_num { get; set; }
public string code { get; set; }
public string currency { get; set; }
public string description { get; set; }
public string haracteristics { get; set; }
public string product_id { get; set; }
public string model { get; set; }
public string name { get; set; }
public string price { get; set; }
public string price_dds { get; set; }
public string picture_url { get; set; }
public Product()
{
available = "N/A";
catalog_num = "N/A";
code = "N/A";
currency = "N/A";
description = "N/A";
haracteristics = "N/A";
product_id = "N/A";
model = "N/A";
name = "N/A";
price = "N/A";
price_dds = "N/A";
picture_url = "N/A";
}
}
and the Parser method like this :
public static void FillVendor(string url)
{
string result = GetXMLstream(url);
var vendors = new VendorList();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba.xml");
XDocument d = XDocument.Load(#"D:/proba/proba.xml");
vendors.vendor_list = (from c in d.Descendants("vendor")
select new Vendor()
{
id = c.Element("id").Value,
name = c.Element("name").Value
}).ToList<Vendor>();
foreach (Vendor v in vendors.vendor_list)
{
FillGroups(v.id);
}
}
public static void FillGroups(string vendorID)
{
string url = "main address" + vendorID;
string result = GetXMLstream(url);
var group = new Vendor();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba1.xml");
XDocument d = XDocument.Load(#"D:/proba/proba1.xml");
group.groups_list = (from g in d.Descendants("group")
select new Group()
{
id = g.Element("id").Value,
name = g.Element("name").Value
}).ToList<Group>();
foreach (Group g in group.groups_list)
{
FillSubGroup(vendorID, g.id);
}
}
public static void FillSubGroup(string vendorID, string groupID)
{
string url = "main address" + vendorID+"/"+groupID;
string result = GetXMLstream(url);
var subgroup = new Group();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba2.xml");
XDocument d = XDocument.Load(#"D:/proba/proba2.xml");
subgroup.subgroup_list = (from g in d.Descendants("subgroup")
select new SubGroup()
{
id = g.Element("id").Value,
name = g.Element("name").Value
}).ToList<SubGroup>();
foreach (SubGroup sb in subgroup.subgroup_list)
{
FillProduct(vendorID, groupID, sb.id);
}
}
public static void FillProduct(string vendorID,string groupID,string subgroupID)
{
string url = "main address" + vendorID + "/" + groupID+"/"+subgroupID;
string result = GetXMLstream(url);
var product = new SubGroup();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba2.xml");
XDocument d = XDocument.Load(#"D:/proba/proba2.xml");
product.product_list = (from g in d.Descendants("subgroup")
select new Product()
{
available = g.Element("available").Value,
catalog_num = g.Element("catalog_num").Value,
code = g.Element("code").Value,
currency = g.Element("currency").Value,
description = g.Element("description").Value,
haracteristics = g.Element("haracteristics").Value,
product_id = g.Element("id").Value,
model = g.Element("model").Value,
name = g.Element("name").Value,
price = g.Element("price").Value,
price_dds = g.Element("price_dds").Value,
picture_url = g.Element("small_picture").Value,
}).ToList<Product>();
}
But after finishing parsing I try to check if my Lists are populated with objects, but I get an error which says that they are null "NullReferenceException"
So my question is did I make classes properly and is my parsing method right ( you can suggest how to parse the xml without creating a file on my computer) and if I didn't where is my mistake and how should I make it work properly?
Thanks in advance!

modify this line add 's'( vendor -> vendors)
-> vendors.vendor_list = (from c in d.Descendants("vendor")
and the same case for group -> groups

Instead of making the classes yourself.
Create a properly formatted XML Schema either manually or with Visual Studio and then from that XSD File you've created generate your C# Classes.

Related

How to convert data from mongodb document to List

I Create a web-site and I have a problem. When I'm tring to get datas from mongodb and convert it to list, I have an error "Cannot apply indexing with [] to an expression of type 'CategoryModel'"
this is classes
public class CategoryModel
{
[BsonId]
public ObjectId id { get; set; }
[BsonElement("title")]
[JsonProperty("title")]
public string Name { get; set; }
[BsonElement("slug")]
public string slug { get; set; }
[BsonElement("__v")]
public int __v { get; set; }
}
public class ProductsModel
{
[BsonId]
public ObjectId id { get; set; }
[BsonElement("title")]
public string Name { get; set; }
[BsonElement("desc")]
public string Desc { get; set; }
[BsonElement("price")]
public int price { get; set; }
[BsonElement("category")]
public CategoryModel category { get; set; }
[BsonElement("image")]
public string ImageURL { get; set; }
}
this is my conntroller
public class ProductsController:Controller
{
private readonly IConfiguration _configuration;
List<CategoryModel> categoriesList = new List<CategoryModel>();
List<ProductsModel> productsList = new List<ProductsModel>();
[HttpGet("products")]
public async Task<IActionResult> Product()
{
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("cmscart");
var collection = database.GetCollection<CategoryModel>("categories");
var result = await collection.Find(new BsonDocument()).ToListAsync();
foreach (var item in result)
{
categoriesList.Add(new CategoryModel() { Name = (string)item["[title]"] }); //here I have an error
}
//products
var client2 = new MongoClient("mongodb://localhost:27017");
var database2 = client2.GetDatabase("cmscart");
var collection2 = database2.GetCollection<ProductsModel>("products");
var result2 = await collection2.Find(new BsonDocument()).ToListAsync();
foreach (var item2 in result2)
{
productsList.Add(new ProductsModel() { Name = (string)item2["title"], Desc = (string)item2["desc"], price = (int)item2["price"], ImageURL = (string)item2["image"] }); // and here I have an error
}
return View(categoryProduct);
}
}
I found a lot of solutions but I don't understand why this error is appear, because if this trick do with SQL then I don't have this error
You should be able to use the deserialized object directly:
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("cmscart");
var collection = database.GetCollection<CategoryModel>("categories");
List<CategoryModel> categoriesList = await collection.Find(new BsonDocument()).ToListAsync();
//products
var collection2 = database2.GetCollection<ProductsModel>("products");
List<ProductsModel> products = await collection2.Find(new BsonDocument()).ToListAsync();
Also, don't use class properties for local variables, declare everything in the innermost scope possible (in general). Ideally the MongoClient or Database would be injected into the class constructor too. You don't want to be instantiating them in an action method and you definitely don't want configuration values in there.

Setting properties for object only when present in database

I have an object that is supposed to describe some customer with these values:
{
public class CustomerDescription
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public string StreetName { get; set; } = "";
public string HouseNumber { get; set; } = "";
public string PostalPlace { get; set; } = "";
public string PostCode { get; set; } = "";
public string CountryCode { get; set; } = "";
public string EMail { get; set; } = "";
public string PhoneNumber { get; set; } = "";
}
}
These values are retrieved from a database of customers and will be used to create an XML file which will be sent via SOAP to another database. However, not all these values are always present. For example one customer may not have the country code in the database. In this case I would like to not include this value at all in the XML file I am sending. Here is an example of how I create the XML elements:
new XElement("address",
new XElement("address1", CustomerDescription.StreetName + " " + cabCustomerDescription.HouseNumber),
new XElement("postalCode", cUstomerDescription.PostCode),
new XElement("city", customerDescription.PostalPlace),
new XElement("countryCode", customerDescription.CountryCode))
)
This is done for all the properties in CustomerDescription. My question is, how can I do this so that if a value is not present in the database, then this value is not included in the XML file? I would not like it to be empty, like <countryCode></countryCode>, but rather not present at all.
You just need to use ternary operator if value is null from database dont include in x element.
Ex:Explicitely i did StreetName null an dcheck if null then dont add in xml file.
CustomerDescription t = new CustomerDescription();
t.StreetName = null;
var abc = new XElement("address", t.StreetName != null ? new XElement("address1", t.StreetName + " " + t.HouseNumber) : null,
new XElement("postalCode", t.PostCode),
new XElement("city", t.PostalPlace),
new XElement("countryCode", t.CountryCode));

C# populating class array from xml linq results in FormatException

I'm getting a FormatException error "Input string was not in a correct format." error...
Employee class is all strings except the Status(int), Type(int) and Displayed(bool)
public partial class Version2 : System.Web.UI.Page {
private Employee[] Employees;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
XDocument xdoc = XDocument.Load(Server.MapPath("~/")+ "V2Employees.xml");
IEnumerable<XElement> emps = xdoc.Root.Descendants();
Employees = emps.Select(x => new Employee()
{
Employee_ID = x.Attribute("id").Value,
First_Name = x.Element("First_Name").Value,
Last_Name = x.Element("Last_Name").Value,
Classification_Group = x.Element("Classification_Group").Value,
Classification_Level = x.Element("Classification_Level").Value,
Postion_Number = x.Element("Postion_Number").Value,
English_Title = x.Element("English_Title").Value,
French_Title = x.Element("French_Title").Value,
Location = x.Element("Location").Value,
Language = x.Element("Language").Value,
Department_ID = x.Element("Department_ID").Value,
Status = int.Parse(x.Element("Status").ToString()),
Type = int.Parse(x.Element("Type").ToString()),
Displayed = false}).ToArray();
}
Example XML going in:
<?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee id="1">
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
<Classification_Group>AA</Classification_Group>
<Classification_Level>02</Classification_Level>
<Postion_Number>12345</Postion_Number>
<English_Title>Bob</English_Title>
<French_Title></French_Title>
<Location>Null Island</Location>
<Language>English</Language>
<Department_ID>000001</Department_ID>
<Status>1</Status>
<Type>4</Type>
</employee>
</employees>
By calling ToString on an XElement, you're ending up with the whole element as a string, e.g.
<Status>4</Status>
... and trying to parse that as an int. I would strongly suggest:
Just using the Value property or a cast to string when you want the value of an element as a string (no need for a ToString call afterwards)
Using a cast to int when you want to convert an XElement's value to an integer
I would write your Select as:
// Property names adjusted to .NET naming conventions, type of
// PositionNumber changed to an int.
Employees = emps.Select(x => new Employee
{
EmployeeId = (string) x.Attribute("id"),
FirstName = (string) x.Element("First_Name"),
LastName = (string) x.Element("Last_Name"),
ClassificationGroup = (string) x.Element("Classification_Group"),
ClassificationLevel = (string) x.Element("Classification_Level"),
PositionNumber = (int) x.Element("Postion_Number"),
EnglishTitle = (string) x.Element("English_Title"),
FrenchTitle = (string) x.Element("French_Title"),
Location = (string) x.Element("Location"),
Language = (string) x.Element("Language"),
DepartmentId = (string) x.Element("Department_ID"),
Status = (int) x.Element("Status"),
Type = (int) x.Element("Type"),
Displayed = false
}
You might want to use .Value instead of the cast to string, which will mean you get a NullReferenceException if the element is missing the appropriate sub-element. You may want to use the above form instead, then have a Validate method to check that it has all the required information - that would probably allow you to report errors more clearly:
You could say which property is missing rather than just getting an exception
You could report multiple missing properties in a single error message
See code below. The fix was : xdoc.Root.Descendants("employee"); I also made other improvements
public class Employee
{
static Employee[] Employees = null;
const string FILENAME = #"c:\temp\test.xml";
public static Boolean IsPostBack = false;
string Employee_ID { get; set; }
string First_Name { get; set; }
string Last_Name { get; set; }
string Classification_Group { get; set; }
string Classification_Level { get; set; }
string Postion_Number { get; set; }
string English_Title { get; set; }
string French_Title { get; set; }
string Location { get; set; }
string Language { get; set; }
string Department_ID { get; set; }
int Status { get; set; }
int Type { get; set; }
Boolean Displayed { get; set; }
//protected void Page_Load(object sender, EventArgs e)
public void Page_Load()
{
if (!IsPostBack)
{
XDocument xdoc = XDocument.Load(FILENAME);
IEnumerable<XElement> emps = xdoc.Root.Descendants("employee");
Employees = emps.Select(x => new Employee()
{
Employee_ID = (string)x.Attribute("id"),
First_Name = (string)x.Element("First_Name"),
Last_Name = (string)x.Element("Last_Name"),
Classification_Group = (string)x.Element("Classification_Group"),
Classification_Level = (string)x.Element("Classification_Level"),
Postion_Number = (string)x.Element("Postion_Number"),
English_Title = (string)x.Element("English_Title"),
French_Title = (string)x.Element("French_Title"),
Location = (string)x.Element("Location"),
Language = (string)x.Element("Language"),
Department_ID = (string)x.Element("Department_ID"),
Status = (int)x.Element("Status"),
Type = (int)x.Element("Type"),
Displayed = false
}).ToArray();
}
}
}

How to return a specified Json format in c#

Hi all I am trying to build a quiz application using angular JS, I am having two tables Questions and Answers and the code is as follows
public class Question
{
public int QuestionID { get; set; }
public string QuestionName { get; set; }
public List<Options> Options = new List<Options>();
}
public class Options
{
public int AnswerId { get; set; }
public int QuestionID { get; set; }
public string Answer { get; set; }
public bool isAnswer { get; set; }
}
public static class QuizDetails
{
public static string GetQuiz()
{
Dictionary<int, List<Question>> displayQuestion = new Dictionary<int, List<Question>>();
//List<Quiz> quiz = new List<Quiz>();
//Options op1 = new Options();
dbDataContext db = new dbDataContext();
var v = (from op in db.QUESTIONs
join pg in db.ANSWERs on op.QUESTION_ID equals pg.QUESTION_ID
select new { Id = op.QUESTION_ID, Name = op.QUESTION_NAME, pg.ANSWER_ID, pg.QUESTION_ID, pg.ANSWER_DESCRIPTION, pg.CORRECT_ANSWER }).ToList();
return JsonConvert.SerializeObject(v);
}
}
This is my reference code for building the application
http://www.codeproject.com/Articles/860024/Quiz-Application-in-AngularJs, how can I return the JSON format as per the code written in the JS files can some one help me
Right now GetQuiz returns a string that represents an object. Your client doesn't really know what the string contains, it just handles it as a normal string.
You can either return it in another way, for example:
return new HttpResponseMessage
{
Content = new StringContent(
JsonConvert.SerializeObject(v),
System.Text.Encoding.UTF8,
"application/json")
};
If you want to keep returning it as a string you will have to manually deserialize it in the client:
var object = angular.fromJson(returnedData);

XPath matches for a XML serialized DataContract class

I have a DataContract class MyDataContract. I am serializing it to a XML file. Later, at a totally different "place" in my application I am loading this file. There I want to verify to load only, if the content of the file matches special conditions. Let's say I store a person and the association to the person's vehicle assuming a person can own just one vehicle. :-)
Here the DataContract classes:
namespace Test.DataContracts
{
[DataContract]
public class MyDataContract
{
[DataMember]
public string Identifier { get; set; }
[DataMember]
public Common.Person Person { get; set; }
[DataMember]
public Common.Vehicle Vehicle { get; set; }
}
}
namespace Test.DataContracts.Common
{
[DataContract]
public class Person
{
[DataMember]
public Global.Gender Gender { get; set; }
[DataMember]
public string Info { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Vehicle
{
[DataMember]
public string Info { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Vendor { get; set; }
}
}
namespace Test.DataContracts.Global
{
[DataContract]
public class Gender
{
[DataMember]
public int Type { get; set; }
[DataMember]
public string Name { get; set; }
}
}
Results in the following serialized XML:
<?xml version="1.0" encoding="utf-8"?>
<MyDataContract xmlns="http://schemas.datacontract.org/2004/07/Test.DataContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Identifier>123456789</Identifier>
<Person xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
<a:Gender xmlns:b="http://schemas.datacontract.org/2004/07/Test.DataContracts.Global">
<b:Name>Female</b:Name>
<b:Type>0</b:Type>
</a:Gender>
<a:Info>She is a beautiful lady.</a:Info>
<a:Name>Jane Doe</a:Name>
</Person>
<Vehicle xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
<a:Info>A super great car.</a:Info>
<a:Name>Mustang 1983 Turbo GT</a:Name>
<a:Vendor>Ford</a:Vendor>
</Vehicle>
</MyDataContract>
Now I want to filter out only Female (Type = 0) persons that own any Ford (Vendor = Ford) vehicle. I tried the following, but it always results in false for the matches.
string path = #"c:\janedoe.xml";
var xmlDoc = new XmlDocument();
xmlDoc.Load(path);
XmlNodeList xNodes = xmlDoc.SelectNodes(#"//namespace::*[not(. = ../../namespace::*)]");
var xNamespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
foreach (XmlNode node in xNodes)
{
if (!string.IsNullOrWhiteSpace(xNamespaceManager.LookupNamespace(node.LocalName))) continue;
xNamespaceManager.AddNamespace(node.LocalName, node.Value);
}
using (var fs = new FileStream(path, FileMode.Open))
{
var xDocument = new XPathDocument(fs);
var xNavigator = xDocument.CreateNavigator();
XPathExpression exp1 = xNavigator.Compile(string.Format("MyDataContract/Person/Gender/Type/descendant::*[contains(text(), '{0}')]", "0"));
exp1.SetContext(xNamespaceManager);
XPathExpression exp2 = xNavigator.Compile(string.Format("MyDataContract/Vehicle/Vendor/descendant::*[contains(text(), '{0}')]", "Ford"));
exp2.SetContext(xNamespaceManager);
if (xNavigator.Matches(exp1) && xNavigator.Matches(exp2))
{
Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}
}
Can anyone help?
UPDATE 1 - I have changed the code using XmlNamespaceManager. But still results in false when executing xNavigator.Matches(exp1).
If you have XDocument it is easier to use LINQ-to-XML:
var xdoc = XDocument.Load(memorystream);
// Making it simple, grab the first
var type = xdoc.Descendants(XName.Get("Type","http://schemas.datacontract.org/2004/07/Test.DataContracts.Global")).FirstOrDefault();
var vendor = xdoc.Descendants(XName.Get("Vendor", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Common")).FirstOrDefault();
string path = "blah";
if (type != null && type.Value == "0" && vendor != null && vendor.Value == "Ford")
{
Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}
If you are sure that XPath is the only solution you need:
using System.Xml.XPath;
var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("a", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Global");
var name = document.XPathSelectElement("path", namespaceManager).Value;

Categories

Resources