How can I use XML as a data source in WebAPI .NET - c#

I'm having some problems with my WebAPI. I have followed the guide from Microsoft and it works: https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api?fbclid=IwAR0tEMDMX3URn2YO0pwnTTAbY2RuGkdu-HUobznac4Lwus6rOVPSeiX-lFs
Now I want to be able to get data from my XML file instead of the hardcoded values the guide use. I have tried to search for this already but not sure I find the right thing. How can I do this?
Code I have tried:
public IEnumerable<Book> GetAllProducts()
{
XDocument doc = XDocument.Load("C:\\Users\\Name\\Desktop\\products.xml");
foreach (XElement element in doc.Descendants("Catalog")
.Descendants("Product"))
{
Product product = new Product();
product.Id = element.Element("Id").Value;
product.Name = element.Element("Name").Value;
product.Category = element.Element("Category").Value;
product.Added_Date = element.Element("Added_Date").Value;//this will not work because its not a string
product.Price = element.Element("Price").Value;//this will not work because its not a string
products.Add(product);
}
return products;
}
XML Code:
<?xml version="1.0"?>
<catalog>
<product id="P1">
<name>Royal Gala</name>
<category>Apple</category>
<country>New Zeeland</country>
<price>33.00</price>
<added_date>2011-01-11</added_date>
<description>This is a lovely red apple.</description>
</product>
<product id="P2">
<name>Granny Smith</name>
<category>Apple</category>
<country>Australia</country>
<price>33.00</price>
<added_date>2013-12-25</added_date>
<description>This is a lovely green apple.</description>
</product>
</catalog>

You just need to parse string to a specific type
Product product = new Product();
//..
product.Added_Date = DateTime.Parse(element.Element("Added_Date").Value);
product.Price = double.Parse(element.Element("Price").Value); //or float, or decimal
And consider using Elements method instead of Descendants cause last one returns all children and their inner children and so on and Elements returns first level children
var productNodes = doc.Root.Elements("product"); //note this is case sensitive
foreach (XElement element in productNodes) {
Product product = new Product();
//..
}

Related

How to get different values with same name from xml with linq

XML - Code:
<Store>
<Products>
<Product id="PROD01">
<Title>Product 1</Title>
<Description><![CDATA[Product <b>1</b> description]]></Description>
<Image>prod01.gif</Image>
<Specs>
<Spec>Good computer</Spec>
<Spec>Good display</Spec>
<Spec>Latest version</Spec>
</Specs>
<Availability>same day</Availability>
</Product>
<Product id="PROD02">
<Title>Product 2</Title>
<Description><![CDATA[Product <b>2</b> description]]></Description>
<Image>prod01.gif</Image>
<Specs>
<Spec>Good computer</Spec>
<Spec>Soon available</Spec>
</Specs>
<Availability>next day</Availability>
</Product>
</Products>
</Store>
C# - Code:
public List<DetailList> GetDetails()
{
DetailList d = new DetailList();
List<DetailList> DetailLists =
(from product in xdocList.Descendants("Product")
join detail in xdocDetail.Descendants("Product")
on (string)product.Attribute("id") equals (string)detail.Attribute("id")
into outerProducts
from outerProduct in outerProducts
select new DetailList
{
Detail1 = (string)product.Attribute("id"),
Detail2 = (string)product.Element("Title"),
Detail3 = (string)product.Element("Description"),
Detail4 = (string)product.Element("Image"),
Detail5 = (string)outerProduct.Elements("Specs")
Detail6 = (string)outerProduct.Element("Availability"),
Detail7 = (string)product.Element("Price"),
}).ToList();
return DetailLists;
}
Output: Good computerGood displayLatest version
But wanted output is:
Good computer
Good display
Latest version
For output I used asp:repeater. I tried to add tags like < b r/> and much more, but cant'find my mistake, how to get Spec to three different strings, not only one string. How to achive that?
I am not sure why you are joing the nodes with self, but as per your XML it is not required. You can simply project the elements like this:-
public static List<DetailList> GetDetails(XDocument xdocList)
{
DetailList d = new DetailList();
List<DetailList> DetailLists = (from product in xdocList.Descendants("Product")
select new DetailList
{
Detail1 = ((string)product.Attribute("id")),
Detail2 = ((string)product.Element("Title")),
Detail3 = ((string)product.Element("Description")),
Detail4 = ((string)product.Element("Image")),
Detail5 = product.Element("Specs")
.Elements("Spec")
.Select(x => (string)x).ToList(),
Detail6 = ((string)product.Element("Availability")),
Detail7 = ((string)product.Element("Price")),
}).ToList();
return DetailLists;
}
Since you need all the Specs separately, you should have a collection of string and not just string. So the datatype of property Detail5 should be:-
List<string> or string[]
Friend got solution from Rahul
Detail5 = String.Join("<br/>", outerProduct.Element("Specs").Elements("Spec").Select(x => (string)x).ToList()),
Thanks #Rahul Singh

how to print the innertext of an element based on attribute search of a particular node using LINQ?

**I have an XML like this-
<?xml version="1.0" encoding="UTF-8"?>
<Tool_Parent>
<tool name="ABCD" id="226">
<category>Centralized</category>
<extension_id>0</extension_id>
<uses_ids>16824943 16824944</uses_ids>
</tool>
<tool name="EFGH" id="228">
<category>Automated</category>
<extension_id>0</extension_id>
<uses_ids>92440 16824</uses_ids>
</tool>
</Tool_Parent>
Based on the id of tool i want to print the uses_ids value,i.e if i search for 228 i should get 92440 16824.
I had tried like-
var toolData = (from toolElement in doc.Descendants("tool")
select new Tool_poco
{
a_Name = tool.Attribute("name").Value,
a_Id = tool.Attribute("id").Value,
e_ExtensionId = tool.Element("extension_id").Value,
e_UsesIds =tool.Element("uses_parm_ids").Value
});
where Tool_poco is a poco class for tool node containing declaration for member variable.
Now I want to get information related to a particular tool id in toolData variable.How to do it?
Note: I have variable like-
searched_Tool_id = Tool_Id_txtBx.Text.ToString();
Please let me know a way through which i can modify my above query for toolData.**
You can modify your query as
Tool_poco toolData = (from el in xelement.Elements("Employee")
where (string)el.Attribute("id") == "226"
select new Tool_poco
{
a_Name = el.Attribute("name").Value,
a_Id = el.Attribute("id").Value,
e_ExtensionId = el.Element("Name").Value,
e_UsesIds = el.Element("uses_ids").Value
}).FirstOrDefault();
You could start by doing something like this once you have an XDocument object loaded and ready:
var xdoc = XDocument.Parse(
#"<?xml version=""1.0"" encoding=""utf-8""?>
<Tool_Parent>
<tool name=""ABCD"" id=""226"">
<category>Centralized</category>
<extension_id>0</extension_id>
<uses_ids>16824943 16824944</uses_ids>
</tool>
<tool name=""EFGH"" id=""228"">
<category>Automated</category>
<extension_id>0</extension_id>
<uses_ids>92440 16824</uses_ids>
</tool>
</Tool_Parent>");
var root = xdoc.Root; // Got to have that root
if (root != null)
{
var id228query = (from toolElement in root.Elements("tool")
where toolElement.HasAttributes
where toolElement.Attribute("id").Value.Equals("228")
let xElement = toolElement.Element("uses_ids")
where xElement != null
select xElement.Value).FirstOrDefault();
Console.WriteLine(id228query);
Console.Read();
}
Output: 92440 16824
**Note: In reference to your example, one possible reason it was not working
for you could be that your xml references an element with name "uses_ids",
however, your query references an element with a similar, but not exact,
spelling with name "uses_parm_ids".**

Retrieving Multiple Items from an XML File using LINQ to XML with C#

I'm new to LINQ to XML and I'm having problems writing C# to retrieve multiple items from an XML file, i.e. in the code sample below. I would like to go through the file and retrieve each OrderProduct id=??? and get the information in Quantities and Product. I can retrieve a single order only, but not if more than one is in the file.
This is the C# code I'm using which only retrieves the first order.
xelement = XElement.Load (orderXML);
IEnumerable<XElement> OrderXml = xelement.Elements ();
foreach (var order in OrderXml.Elements ("OrderProducts"))
{
m_productOrderID = order.Element ("OrderProduct").Attribute ("id").Value;
m_productName = order.Element ("OrderProduct").Element ("Product").Element ("Name").Value;
m_productCatalogNumber = order.Element ("OrderProduct").Element ("Product").Element ("CatalogNumber").Value;
m_productQuantity = order.Element ("OrderProduct").Element ("Quantities").Element ("NumberOfCopies").Value;
}
The XML file:
<?xml version="1.0" encoding="utf-16"?>
<OrderXml>
<Order>
<OrderProducts>
<OrderProduct id="569">
<Quantities>
<NumberOfRecipients>1</NumberOfRecipients>
<NumberOfCopies>1</NumberOfCopies>
<TotalUnits>1</TotalUnits>
</Quantities>
<Product id="444">
<Name>Product 1</Name>
<CatalogNumber>20130621-001</CatalogNumber>
</Product>
</OrderProduct>
<OrderProduct id="570">
<Quantities>
<NumberOfRecipients>1</NumberOfRecipients>
<NumberOfCopies>100</NumberOfCopies>
<TotalUnits>100</TotalUnits>
</Quantities>
<Product id="258">
<Name>Product 2</Name>
<CatalogNumber>20130621-002</CatalogNumber>
</Product>
</OrderProduct>
</OrderProducts>
</Order>
</OrderXml>
from op in xdoc.Descendants("OrderProduct")
let q = op.Element("Quantities")
let p = op.Element("Product")
select new {
Id = (int)op.Attribute("id"),
Quantities = new {
NumberOfRecipients = (int)q.Element("NumberOfRecipients"),
NumberOfCopies = (int)q.Element("NumberOfCopies"),
TotalUnits = (int)q.Element("TotalUnits")
},
Product = new {
Id = (int)p.Attribute("id"),
Name = (string)p.Element("Name"),
CatalogNumber = (string)p.Element("CatalogNumber")
}
}
Then getting single order product:
var orderProduct = query.FirstOrDefault(x => x.Id == yourId);
if (orderProduct != null)
// ...
Getting all ids:
var ids = xdoc.Descendants("OrderProduct")
.Select(op => (int)op.Attribute("id"));
BTW Next time provide code which you already have

Querying XML elements with identical name with Linq

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<ProductTypes>
<ProductType Name="MyProduct">
<Amount>100</Amount>
<Pattern Length="1" AllowedCharacters="ABCD"/>
<Pattern Length="7" AllowedCharacters="EFGH"/>
</ProductType>
</ProductTypes>
Using Linq to XML I can successfully extract information from any number of the element ProductType. However I also need the information from all of the elements Pattern.
XElement xml = XElement.Load("pattern_config.xml");
var productTypes = from productType in xml.Elements("ProductType")
select new {
Name = productType.Attribute("Name").Value,
Amount = Convert.ToInt32(productType.Element("Amount").Value)
// How to get all Pattern elements from that ProductType?
};
How can I do this? Or would you recommend another way accessing this XML?
You can nest queries.
var productTypes = from productType in xml.Elements("ProductType")
select new {
Name = productType.Attribute("Name").Value,
Amount = Convert.ToInt32(productType.Element("Amount").Value),
// How to get all Pattern elements from that ProductType?
Patterns = from patt in productType.Elements("Pattern")
select new { Length = int.Parse(patt.Attribute("Length").Value),
.... }
};

Update XML file with Linq

I'm having trouble trying to update my xml file with a new value. I have a class Person, which only contains 2 strings, name and description. I populate this list and write it as an XML file. Then I populate a new list, which contains many of the same names, but some of them contains descriptions that the other list did not contain. How can I check if the name in the current XML file contains a value other than "no description", which is the default for "nothing"?
Part of the xml file:
<?xml version="1.0" encoding="utf-8"?>
<Names>
<Person ID="2">
<Name>Aaron</Name>
<Description>No description</Description>
</Person>
<Person ID="2">
<Name>Abdi</Name>
<Description>No description</Description>
</Person>
</Names>
And this is the method for writing the list to the xml file:
public static void SaveAllNames(List<Person> names)
{
XDocument data = XDocument.Load(#"xml\boys\Names.xml");
foreach (Person person in names)
{
XElement newPerson = new XElement("Person",
new XElement("Name", person.Name),
new XElement("Description", person.Description)
);
newPerson.SetAttributeValue("ID", GetNextAvailableID());
data.Element("Names").Add(newPerson);
}
data.Save(#"xml\boys\Names.xml");
}
In the foreach loop how do I check if the person's name is already there, and then check if the description is something other than "no description", and if it is, update it with the new information?
I'm not sure I understand properly what you want, but I'm assuming you want to update the description only when the name is already there and the description is currently No description (which you should probably change to an empty string, BTW).
You could put all the Persons into a Dictionary based by name:
var doc = …;
var persons = doc.Root.Elements()
.ToDictionary(x => (string)x.Element("Name"), x => x);
and then query it:
if (persons.ContainsKey(name))
{
var description = persons[name].Element("Description");
if (description.Value == "No description")
description.Value = newDescription;
}
That is, if you care about performance. If you don't, you don't need the dictionary:
var person = doc.Root.Elements("Person")
.SingleOrDefault(x => (string)x.Element("Name") == name);
if (person != null)
{
var description = person.Element("Description");
if (description.Value == "No description")
description.Value = newDescription;
}
You can use the Nodes-Method on XElement and check manually.
But i will advise you to use the XPathEvaluate-Extension Method
For XPath expression take a look at:
How to check if an element exists in the xml using xpath?
I think you could create a peoplelist which only contains people not in the xml.
like ↓
var containlist = (from p in data.Descendants("Name") select p.Value).ToList();
var result = (from p in peoplelist where !containlist.Contains(p.Name) select p).ToList();
so that , you would no need to change anything with your exist method ...
just call it after..
SaveAllNames(result);

Categories

Resources