Linq on Complex XML - c#

Hi I Have the following XML
<Feed>
<Control>
<Username>Fnol13</Username>
<Password>12345</Password>
</Control>
<Repairer>
<RepairerName>Test</RepairerName>
<RepairerAddress>Test</RepairerAddress>
<RepairerTelephone>Test</RepairerTelephone>
</Repairer>
</Feed>
And a Model Class Containing following Properties
[Serializable]
public class Job {
public string Username { get; set; }
public string Password { get; set; }
public string Reference { get; set; }
public string RepairerAddress { get; set; }
public string RepairerTelephone { get; set; }
}
I am using following Linq Query to Extract Data from XML
var results = from job in xmlDoc.Descendants("Control")
select new Job {
Username = (string)job.Element("Username").Value,
Password = (string)job.Element("Password").Value
};
// Here I want to add as well Descendants("Repairer") using same query
return results.ToList();
Problem is that can return Descendants("Control") However I would like to get also Descendants("Repairer") and return in a same list as my model is showing. Could you please help me to write Linq Query and I am confirming you I am very new in Linq.

Your Model Job looks confusing to me because you may have multiple Control &
Repairer nodes in which case it should map to a collection. Anyways for current XML I assume you need the elements of first Repairer node, you can achieve it like this:-
var results = from job in xmlDoc.Root.Elements("Control")
let Repairer = job.Parent.Elements("Repairer").FirstOrDefault()
select new Job
{
Username = (string)job.Element("Username"),
Password = (string)job.Element("Password"),
RepairerName = (string)Repairer.Element("RepairerName"),
RepairerAddress = (string)Repairer.Element("RepairerAddress"),
RepairerTelephone = (string)Repairer.Element("RepairerTelephone")
};
Also, note (string)job.Element("Username") will give you the node value. There is no need to call the Value property.
Update:
You can use XDocument when working with LINQ-to-XML:-
XDocument xmlDoc = XDocument.Load(XMLpath);

You could do something like -
Reference = (string)job.Parent.Descendants("Repairer").FirstOrDefault().Element("RepairerAddress").Value;

Related

LINQ convert SQL server Datetime to string using a DTO

I've been tasked to add a page to an API that we didn't build but are tasked to work on. The API is using C# MVC5. I don't really know MVC5, and but I'm attempting to keep the same design pattern that the rest of the API is using. I have to add functionality that will allow a user on the front end to upload a file to a server that will be processed for inserting into a SQL Server DB. This functionality will also return a list of all the files names and status of the imported files from a table in the DB.
The issue I'm having is converting a Datetime to a string in the LINQ query that is pulling the list of files.
I've attempted to try using this answer LINQ convert DateTime to string, but with no luck.
This is what I have so far, I've marked the line that is causing the issue:
[Route("ImportLogs", Name ="GetImportLogs")]
[HttpGet]
[ResponseType(typeof(List<IHttpActionResult>))]
public IHttpActionResult GetImportLogs()
{
var query =
from dbitem in db.ImportLogs
orderby dbitem.import_log_id
select new ImportLogDto()
{
Id = dbitem.import_log_id,
FileName = dbitem.import_file_name,
ImportTimeStamp = dbitem.import_timeStamp,
ImportStatus = dbitem.import_status,
ImportMessage = dbitem.import_message
};
query.ToList()
.Select(o => new ImportLogDto
{
Id = o.Id,
FileName = o.FileName,
ImportMessage = o.ImportMessage,
ImportStatus = o.ImportStatus,
ImportTimeStamp = o.ImportTimeStamp.ToString() //PROBLEM LINE
});
return Ok(query);
}
The error that I'm getting is
Cannot implicitly convert type 'string' to 'System.DateTime'
What am doing wrong? Any help would be appreciated. TIA.
EDIT:
Here is the DTO:
public class ImportLogDto
{
public int Id { get; set; }
public string FileName { get; set; }
public DateTime ImportTimeStamp { get; set; }
public string ImportStatus { get; set; }
public string ImportMessage { get; set; }
}
You are trying to assign a string into a DateTime so you get that exception. If you want to cast it to a string change your model as follows:
public class ImportLogDto
{
public int Id { get; set; }
public string FileName { get; set; }
public string ImportTimeStamp { get; set; } // Changed type
public string ImportStatus { get; set; }
public string ImportMessage { get; set; }
}
And then your query:
var query = (from dbitem in db.ImportLogs
orderby dbitem.import_log_id
select new {
Idbitem.import_log_id,
dbitem.import_file_name,
dbitem.import_timeStamp, // Still DateTime
dbitem.import_status,
dbitem.import_message
}).AsEnumerable(); // Retrieved from Database
.Select(o => new ImportLogDto
{
Id = o.import_log_id,
FileName = o.import_file_name,
ImportMessage = o.import_message,
ImportStatus = o.import_status,
ImportTimeStamp = o.import_timeStamp.ToString() // Changes to string
});
If you wnat to change the format of the DateTime for the API then then use its overload of ToString and specify a format:
ImportTimeStamp = o.ImportTimeStamp.ToString("dd/MM/yyyy HH24:MI:ss")
For more on the overload read: Custom Date and Time Format Strings
Your types are already DateTime, and since these types are tied to the backing data* you probably shouldn't change them. But where you return the values on the API you can really return anything you want. So an anonymous type could be a quick solution:
.Select(o => new // <--- notice no type definition here
{
Id = o.Id,
FileName = o.FileName,
ImportMessage = o.ImportMessage,
ImportStatus = o.ImportStatus,
ImportTimeStamp = o.ImportTimeStamp.ToString()
})
The compiler will know based on the type returned by .ToString() that you want ImportTimeStamp to be a string. You can then add formatters to .ToString() to customize the output however you like.
*If your DTO isn't actually tied to the database then you can change the type there from DateTime to string, of course. It's not really clear from the context of the code shown whether these are data DTOs or application DTOs.

Parse XML string returned from service with C#

I'm trying to parse the following XML string that is being returned from a service.
DataReference.USZipSoapClient blah = new DataReference.USZipSoapClient("USZipSoap");
var results = blah.GetInfoByCity(tbCityName.Text).OuterXml;
returns the following
<NewDataSet xmlns=""><Table><CITY>Marana</CITY><STATE>AZ</STATE><ZIP>85653</ZIP><AREA_CODE>520</AREA_CODE><TIME_ZONE>M</TIME_ZONE></Table></NewDataSet>
I'm having no luck parsing the data:
I just want to display the results like City = Marana, State = AZ etc.
Why not just use XPath?
XmlDocument doc = new XmlDocument()
doc.LoadXml(results); // probably some try-catch here
var city = doc.SelectSingleNode("//CITY").InnerXml; //Handle null as well
You can create a serializing object and then serialize the data you get against that object, i used http://xmltocsharp.azurewebsites.net/ to generate the following xml object:
[XmlRoot(ElementName="Table")]
public class Table {
[XmlElement(ElementName="CITY")]
public string CITY { get; set; }
[XmlElement(ElementName="STATE")]
public string STATE { get; set; }
[XmlElement(ElementName="ZIP")]
public string ZIP { get; set; }
[XmlElement(ElementName="AREA_CODE")]
public string AREA_CODE { get; set; }
[XmlElement(ElementName="TIME_ZONE")]
public string TIME_ZONE { get; set; }
}
[XmlRoot(ElementName="NewDataSet")]
public class NewDataSet {
[XmlElement(ElementName="Table")]
public Table Table { get; set; }
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
}
Then just use the .net XML serializer to cast it to that object and use it in your code.
If you just need a string for visual inspection, a first step could be to convert the XMl to JSON with this code:
http://techhasnoboundary.blogspot.no/2011/08/convert-xml-to-json-using-c.html
Then you could go on by removing braces, replace comma with line break and colon with an equal sign.

creating an array from xml data in c#

I have the following XML returned when I make an API call. I need to create an array that holds the <status> and <description> values for each of <order_status>, <payment_status>, and <fulfillment_status>. Can anyone share some knowledge that would help me?
<orderwave>
<call_result>
<order>
<order_number>17377</order_number>
<orderwave_order_number>RWD-336475921</orderwave_order_number>
<order_status>
<status>active</status>
<description>This order is free to be charged and shipped. It is open in the orderwave workflow.</description>
</order_status>
<payment_status>
<status>declined</status>
<description>This order has been declined by the payment network.</description>
</payment_status>
<fulfillment_status>
<status>not shipped</status>
<description>This order has not been allocated for shipment.</description>
</fulfillment_status>
</order>
</call_result>
<warning_count>0</warning_count>
<warnings/>
<error_count>0</error_count>
<errors/>
</orderwave>
LinqToXML is what you want I believe.
I haven't done this in a while so my syntax might not be perfect.
Something like this:
var xmlSource = contacts.Load(#"../../return.xml");
var q = xmlSource.Descendants("order").SelectMany(x => x.Elements("order_status")
To expand upon my earlier comment, an easy and quick way to do this is with List<T> Class and LINQ to XML.
Use a class to hold the data for each order - for example:
public class Order
{
public int OrderNumber { get; set; }
public string OrderStatus { get; set; }
public string OrderDescription { get; set; }
public string PaymentStatus { get; set; }
public string PaymentDescription { get; set; }
public string FulfillmentStatus { get; set; }
public string FulfillmentDescription { get; set; }
}
Next, you can load the XML into an XDocument, parse it with LINQ to XML and create a List of Order objects:
// Parse the XML string; you can also load the XML from a file.
XDocument xDoc = XDocument.Parse("<orderwave>....</orderwave>");
// Get a collection of elements under each order node
var orders = (from x in xDoc.Descendants("order")
// Use the data for each order node to create a new instance of the order class
select new Order
{
OrderNumber = ConvertTo.Int32(x.Element("order_number").Value),
OrderStatus = x.Element("order_status").Element("status").Value,
OrderDescription = x.Element("order_status").Element("description").Value,
PaymentStatus = x.Element("payment_status").Element("status").Value,
PaymentDescription = x.Element("payment_status").Element("description").Value,
FulfillmentStatus = x.Element("fulfillment_status").Element("status").Value,
FulfillmentDescription = x.Element("fulfillment_status").Element("description").Value
}).ToList();
// Convert the result to a list of Order objects
This is off the top of my head without testing it, but it should point you in the right direction if you want to use Lists instead of arrays.

How to loop through a set of XElements?

http://www.dreamincode.net/forums/xml.php?showuser=335389
Given the XML above, how can I iterate through each element inside of the 'lastvisitors' element, given that each child group is the same with just different values?
//Load latest visitors.
var visitorXML = xml.Element("ipb").Element("profile").Element("latestvisitors");
So now I have captured the XElement containing everything I need. Is there a way to loop through the elements to get what I need?
I have this POCO object called Visitor whose only purpose is to hold the necesary information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SharpDIC.Entities
{
public class Visitor
{
public string ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Photo { get; set; }
public string Visited { get; set; }
}
}
Thanks again for the help.
You can probably just do something like this in Linq:
XDocument xml = XDocument.Parse(xmlString);
var visitors = (from visitor in xml.Descendants("latestvisitors").Descendants("user")
select new Visitor() {
ID = visitor.Element("id").Value,
Name = visitor.Element("name").Value,
Url = visitor.Element("url").Value,
Photo = visitor.Element("photo").Value,
Visited = visitor.Element("visited").Value
});
The only caveat here is that I didn't do any null checking.
Just do a linq query to select all the elements as your object.
var visitors = (from v in xml.Element("ipb").Element("profile")
.Element("latestvisitors").Elements()
select new Visitor {
ID = (string)v.Element("id"),
Name = (string)v.Element("name"),
}).ToList();

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