Xpath Select all subnodes of a group of nodes - c#

How can I select all subnodes of the current node?
XmlNodeList productNodes = doc.DocumentElement.SelectNodes("//PRODUCT");
Parallel.ForEach(productNodes.Cast<XmlNode>(),
(XmlNode productNode) =>
{
string _productMode = XmlUtils.nodeAsString(productNode, "#mode");
Product product = new Product()
{
Mode = XmlUtils.nodeAsString(productNode, "#mode"),
No = XmlUtils.nodeAsString(productNode, "./SUPPLIER_PID"),
DescriptionShort = XmlUtils.nodeAsString(productNode, "./PRODUCT_DETAILS/DESCRIPTION_SHORT"),
DescriptionLong = XmlUtils.nodeAsString(productNode, "./PRODUCT_DETAILS/DESCRIPTION_LONG"),
EANCode = XmlUtils.nodeAsString(productNode, "./PRODUCT_DETAILS/EAN"),
Stock = XmlUtils.nodeAsInt(productNode, "./PRODUCT_DETAILS/STOCK"),
OrderUnit = default(QuantityCodes).FromString(XmlUtils.nodeAsString(productNode, "./PRODUCT_ORDER_DETAILS/ORDER_UNIT")),
ContentUnit = default(QuantityCodes).FromString(XmlUtils.nodeAsString(productNode, "./PRODUCT_ORDER_DETAILS/CONTENT_UNIT")),
Currency = default(CurrencyCodes).FromString(XmlUtils.nodeAsString(productNode, "./PRODUCT_PRICE_DETAILS/PRODUCT_PRICE/PRICE_CURRENCY")),
VAT = XmlUtils.nodeAsInt(productNode, "./PRODUCT_PRICE_DETAILS/PRODUCT_PRICE/TAX"),
};
XmlNodeList MimeNodes = doc.DocumentElement.SelectNodes(".//MIME");
Parallel.ForEach(MimeNodes.Cast<XmlNode>(), (XmlNode MimeNode) =>
{
Mime mime = new Mime()
{
Source = XmlUtils.nodeAsString(MimeNode, "./MIME_SOURCE"),
Type = XmlUtils.nodeAsString(MimeNode, "./MIME_TYPE"),
Purpose = XmlUtils.nodeAsString(MimeNode, "./MIME_PURPOSE")
};
product.Mime.Add(mime);
});......
<PRODUCT mode="new">
<SUPPLIER_PID>1902</SUPPLIER_PID>
<PRODUCT_DETAILS>
<DESCRIPTION_SHORT>NORDEN Table</DESCRIPTION_SHORT>
<DESCRIPTION_LONG>massive birch wood. 135x74cm, 74 cm height by Mikael Warnhammar</DESCRIPTION_LONG>
</PRODUCT_DETAILS>
<PRODUCT_ORDER_DETAILS>
<ORDER_UNIT>C62</ORDER_UNIT>
<CONTENT_UNIT>C62</CONTENT_UNIT>
<NO_CU_PER_OU>1</NO_CU_PER_OU>
</PRODUCT_ORDER_DETAILS>
<PRODUCT_PRICE_DETAILS>
<VALID_START_DATE>2013-05-28</VALID_START_DATE>
<VALID_END_DATE>2013-05-30</VALID_END_DATE>
<PRODUCT_PRICE price_type="nrp">
<PRICE_AMOUNT>33.00</PRICE_AMOUNT>
<PRICE_CURRENCY>EUR</PRICE_CURRENCY>
<LOWER_BOUND>200</LOWER_BOUND>
<TERRITORY>AT</TERRITORY>
</PRODUCT_PRICE>
</PRODUCT_PRICE_DETAILS>
<MIME>
<MIME_SOURCE>http://www.google.de/</MIME_SOURCE>
<MIME_TYPE>text/html</MIME_TYPE>
<MIME_PURPOSE>url</MIME_PURPOSE>
</MIME>
<MIME>
<MIME_SOURCE>http://www.stackoverflow.com</MIME_SOURCE>
<MIME_TYPE>text/html</MIME_TYPE>
<MIME_PURPOSE>url</MIME_PURPOSE>
</MIME>
</PRODUCT>
actually i get all the product nodes and i am trying to get all the mimes sub nodes of each product node to save it.. but when i do i get the mime node repeated.
I dont know much about xpath maybe is because i need to speficy the ancestor node ? i tried using descendant but i am getting the same result.

Try following Xml Linq solution
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> productNodes = doc.Descendants("PRODUCT").ToList();
List<Product> product = productNodes.Select(x => new Product() {
Mode = (string)x.Attribute("mode"),
No = (string)x.Element("SUPPLIER_PID"),
DescriptionShort = (string)x.Descendants("DESCRIPTION_SHORT").FirstOrDefault(),
DescriptionLong = (string)x.Descendants("DESCRIPTION_LONG").FirstOrDefault(),
//EANCode = XmlUtils.nodeAsString(productNode, "./PRODUCT_DETAILS/EAN"),
//Stock = XmlUtils.nodeAsInt(productNode, "./PRODUCT_DETAILS/STOCK"),
OrderUnit = (string)x.Descendants("ORDER_UNIT").FirstOrDefault(),
ContentUnit = (string)x.Descendants("CONTENT_UNIT").FirstOrDefault(),
Currency = (string)x.Descendants("PRICE_CURRENCY").FirstOrDefault(),
mime = x.Elements("MIME").Select(y => new Mime() {
Source = (string)y.Element("MIME_SOURCE"),
Type = (string)y.Element("MIME_TYPE"),
Purpose = (string)y.Element("MIME_PURPOSE")
}).ToList()
}).ToList();
}
}
public class Product
{
public string Mode { get; set;}
public string No { get; set;}
public string DescriptionShort { get; set;}
public string DescriptionLong { get; set;}
public string EANCode { get; set;}
public string OrderUnit { get; set;}
public string ContentUnit { get; set;}
public string Currency { get; set;}
public string Vat { get; set;}
public List<Mime> mime { get; set;}
}
public class Mime
{
public string Source { get; set; }
public string Type { get; set;}
public string Purpose { get; set;}
}
}

Related

C# Parse items from namespace

I have the following XML:
https://pastebin.com/YQBhNzm5
I want to match up the item values with the field values.
XmlDocument xdoc = new XmlDocument();
xdoc.Load(ofd.FileName);
XmlNamespaceManager xmanager = new XmlNamespaceManager(xdoc.NameTable);
xmanager.AddNamespace("ns", "http://www.canto.com/ns/Export/1.0");
var result = xdoc.SelectNodes("//ns:Layout/ns:Fields", xmanager);
foreach(XmlElement item in result)
{
Console.WriteLine(item.InnerText);
}
When I do this I get all the field names in one line. How can I iterate through all fields in layout and go one by one?
I parsed xml using xml linq. First I put items into a dictionary. Then I parsed the fields looking up the uid from the dictionary. I parsed the fields recursively to keep the hierarchy.
It looks like the uid, type, value, and name are always the same for each item, but an item can appear in multiple catalogs with a catalog id and an id.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Layout layout = new Layout(FILENAME);
}
}
public class Layout
{
public string tablename { get; set; }
public List<Field> fields { get; set; }
public Layout layout { get; set; }
public Dictionary<string, Item> dict = new Dictionary<string, Item>();
public Layout() { }
public Layout(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement xLayout = doc.Descendants().Where(x => x.Name.LocalName == "Layout").FirstOrDefault();
XNamespace ns = xLayout.GetNamespaceOfPrefix("ns");
foreach (XElement item in doc.Descendants(ns + "Item"))
{
int catalogid = (int)item.Attribute("catalogid");
int id = (int)item.Attribute("id");
foreach(XElement fieldValue in item.Elements(ns + "FieldValue"))
{
string uid = (string)fieldValue.Attribute("uid");
uid = uid.Replace("{", "");
uid = uid.Replace("}", "");
string innertext = (string)fieldValue;
string displayValue = (string)fieldValue.Attribute("displayValue");
List<string> categoryValues = fieldValue.Elements(ns + "CategoryValue").Select(x => (string)x).ToList();
if (!dict.ContainsKey(uid))
{
Item newItem = new Item() {
catalogidId = new List<KeyValuePair<int, int>>() {new KeyValuePair<int, int>(catalogid, id)},
innertext = innertext,
uid = uid,
displayValue = displayValue,
categoryValues = categoryValues
};
dict.Add(uid, newItem);
}
else
{
dict[uid].catalogidId.Add(new KeyValuePair<int, int>(catalogid, id));
}
}
}
layout = new Layout();
RecursiveParse(ns, xLayout, layout);
}
public void RecursiveParse(XNamespace ns, XElement parent, Layout layout)
{
layout.tablename = (string)parent.Attribute("tableName");
foreach(XElement xField in parent.Element(ns + "Fields").Elements(ns + "Field"))
{
if (layout.fields == null) layout.fields = new List<Field>();
Field newField = new Field();
layout.fields.Add(newField);
newField.uid = (string)xField.Attribute("uid");
newField.uid = newField.uid.Replace("{", "");
newField.uid = newField.uid.Replace("}", "");
newField._type = (int)xField.Attribute("type");
newField.value = (int)xField.Attribute("valueInterpretation");
newField.name = (string)xField.Element(ns + "Name");
if (dict.ContainsKey(newField.uid))
{
newField.items = dict[newField.uid];
}
if (xField.Element(ns + "Layout") != null)
{
Layout newLayout = new Layout();
newField.layout = newLayout;
RecursiveParse(ns, xField.Element(ns + "Layout"), newLayout);
}
}
}
public class Field
{
public string uid { get; set; }
public int _type { get; set; }
public int value { get; set; }
public string name { get; set; }
public Layout layout { get; set; }
public Item items { get; set; }
}
public class Item
{
public List<KeyValuePair<int, int>> catalogidId { get; set; }
public string uid { get; set; }
public string innertext { get; set; }
public string displayValue { get; set; }
public List<string> categoryValues { get; set; }
}
}
}

Deserialize xml into class having string values in xml

Let me explain, there is a database table which has 1 XML column named audits and other common types of column.
so is this possible to deserialize below XML into class.
<?xml version="1.0"?>
<entity type="Order">
<id type="System.Int64">146</id>
<ordernumber type="System.String">OD555</ordernumber>
<audits type='System.String'>
<audit>
<item>
<create timestamp='2017-07-19 10:02:13' userid='23' />
</item>
<invoice>
<create timestamp='2017-07-19 10:03:37' userid='45' />
</invoice>
</audit>
</audits>
</entity>
Class:
public class Order
{
public long id { get; set; }
public string ordernumber { get; set; }
public string audits { get; set; }
}
Modifying your model with the attributes XmlType and XmlAnyElement (requires XmlElement as type)
[XmlType("entity")]
public class Order
{
public long id { get; set; }
public string ordernumber { get; set; }
[XmlAnyElement]
public XmlElement audits { get; set; }
}
allows to deserialize the complete XML string like
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(xmlString);
writer.Flush();
stream.Position = 0;
XmlSerializer serializer = new XmlSerializer(typeof(Order));
Order o = (Order)serializer.Deserialize(stream);
}
Now you are able to get the audits as string like
string auditsString = o.audits.InnerXml;
You can also add a property to your model to simplify the access:
public string auditsString
{
get
{
return audits.InnerXml;
}
}
Try xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication68
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Order order = doc.Descendants("entity").Select(x => new Order()
{
id = (long)x.Element("id"),
ordernumber = (string)x.Element("ordernumber"),
audits = x.Descendants("create").Select(y => (DateTime)y.Attribute("timestamp")).ToList()
}).FirstOrDefault();
}
}
public class Order
{
public long id { get; set; }
public string ordernumber { get; set; }
public List<DateTime> audits { get; set; }
}
}
You can try like this
const string xmlString = #"<columns><column><c1>100</c1><c2>200</c2><cn>300</cn></column><column><c1>111</c1><c2>222</c2><cn>333</cn></column> <column> <c1>MAX Newsletter</c1><c2>OLS Application</c2> <cn>Total funded accounts</cn> </column></columns>";
XDocument doc = XDocument.Parse(xmlString);
if (doc.Root != null)
{
List<Row> items = (from r in doc.Root.Elements("column")
select new Row
{
C1 = (string)r.Element ("C1"),
C2 = (string)r.Element("C2"),
}).ToList();

How can i filter XmlNodeList

I have a set of data xml node list, i want to filter with a particular attributes inner text, I tried with this but nothing worked. My code here and also posting the xml data
string baseName = categoryName.Split(':').Last();
int categoryId = 0;
string xmlFile = File.ReadAllText(Application.StartupPath + #"\EbayCategories\EbayCategories.xml");
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlFile);
XmlNodeList nodeList = xmldoc.SelectNodes("/CategoryArray/Category[CategoryName='" + baseName + "']");
if (nodeList.Count > 0)
{
var memberNames = nodeList.Cast<XmlNode>().Where(node => node.Attributes["CategoryID"].InnerText == "58193").ToList();
categoryId = int.Parse(nodeList[0]["CategoryID"].InnerText);
}
Here is my xml Data. I want to filter CategoryParentID = My Value.
<CategoryArray>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>20081</CategoryID>
<CategoryLevel>1</CategoryLevel>
<CategoryName>Antiques</CategoryName>
<CategoryParentID>20081</CategoryParentID>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>37903</CategoryID>
<CategoryLevel>2</CategoryLevel>
<CategoryName>Antiquities</CategoryName>
<CategoryParentID>20081</CategoryParentID>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>37908</CategoryID>
<CategoryLevel>3</CategoryLevel>
<CategoryName>The Americas</CategoryName>
<CategoryParentID>37903</CategoryParentID>
<LeafCategory>true</LeafCategory>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>162922</CategoryID>
<CategoryLevel>3</CategoryLevel>
<CategoryName>Byzantine</CategoryName>
<CategoryParentID>37903</CategoryParentID>
<LeafCategory>true</LeafCategory>
</Category>
I have done with small changes Removed Attributes
var node = nodeList.Cast<XmlNode>().Where(n => n["CategoryParentID"].InnerText == "58193").Select(x => x["CategoryID"].InnerText).SingleOrDefault();
Perfectly worked!!!
Using Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Category.categories = doc.Descendants("Category").Select(x => new Category() {
BestOfferEnabled = (Boolean)x.Element("BestOfferEnabled"),
AutoPayEnabled = (Boolean)x.Element("BestOfferEnabled"),
CategoryID = (int)x.Element("CategoryID"),
CategoryLevel = (int)x.Element("CategoryLevel"),
CategoryName = (string)x.Element("CategoryName"),
CategoryParentID = (int)x.Element("CategoryParentID"),
}).ToList();
int id = 37903;
Category categoryId = Category.categories.Where(x => x.CategoryParentID == id).FirstOrDefault();
}
}
public class Category
{
public static List<Category> categories { get; set; }
public Boolean BestOfferEnabled { get; set; }
public Boolean AutoPayEnabled { get; set; }
public int CategoryID { get; set; }
public int CategoryLevel { get; set; }
public string CategoryName { get; set; }
public int CategoryParentID { get; set; }
}
}

Using C# to create objects parsing XML

I've spent 1.5 days trying to create an object parsing XML:
<EnvelopeStatus>
<RecipientStatuses>
<RecipientStatus>
<Type>Signer</Type>
<Email>johndoe#gmail.com</Email>
<UserName>Doe, John</UserName>
<Status>Completed</Status>
<CustomFields>
<CustomField>1001</CustomField>
</CustomFields>
</RecipientStatus>
<RecipientStatus>
<Type>Signer</Type>
<Email>maryjane#gmail.com</Email>
<UserName>Jane, Mary</UserName>
<Status>Sent</Status>
<CustomFields>
<CustomField>1002</CustomField>
</CustomFields>
</RecipientStatus>
</RecipientStatuses>
<Status>Completed</Status>
<Id>25b9b7e8-c4c0-4711-a80c-24663f0dc6ed</Id>
<CustomFields>
<CustomField>
<Name>Url</Name>
<Required>False</Required>
<Value>http://google.com</Value>
</CustomField>
<CustomField>
<Name>List</Name>
<Required>False</Required>
<Value>Blue</Value>
</CustomField>
<CustomField>
<Name>ItemId</Name>
<Required>False</Required>
<Value>2</Value>
</CustomField>
</EnvelopeStatus>
RecipientStatuses can contain many RecipientStatus. Inside of RecipientStatus there is a collection of CustomFields. Ideally, the single CustomField would move up to same level as Type, Email, UserName, etc.
Status, ID are under EnvelopeStatus, which also contain a collection of CustomFields. The only node really needed in this collection is 'Value' node, so theoretically, Value could move up to same level as Status and Id.
I have tried many different things and am back to square one. Is there a way to parse this xml, so that properties in these classes are set:
public class Request
{
public string Id { get; set; }
public string Status { get; set; }
public string Url { get; set; }
public string List { get; set; }
public string ItemId { get; set; }
public List<Signer> Signers { get; set; }
}
public class Signer
{
public string Type { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
}
You can do this way :
var doc = XElement.Load("path to your XML file");
var req = new Request
{
Id = (string)doc.Element("Id"),
Status = (string)doc.Element("Status"),
Url = (string)doc.Element("CustomFields")
.Elements("CustomFields")
.Where(cf => (string)cf.Element("Name") == "Url")
.Select(cf => (string)cf.Element("Value"))
.First(),
//Follow `Url` example above to populate `List` and `ItemId` properties
Signers = doc.Elements("RecipientStatuses")
.Elements("RecipientStatus")
.Select(o => new Signer
{
Type = (string)o.Element("Type"),
//Follow `Type` example above to populate `Email` and `UserName` properties
UserId = (int)o.Element("CustomFields").Element("CustomField")
})
.ToList()
};
Alternative form using XPath expressions :
var doc = XElement.Load("path to your XML file");
var req = new Request
{
Id = (string)doc.XPathSelectElement("Id"),
Status = (string)doc.XPathSelectElement("Status"),
Url = (string)doc.XPathSelectElement("CustomFields/CustomFields[Name='Url']/Value"),
//Follow `Url` example above to populate `List` and `ItemId` properties
Signers = doc.XPathSelectElements("RecipientStatuses/RecipientStatus")
.Select(o => new Signer
{
Type = (string)o.Element("Type"),
//Follow `Type` example above to populate `Email` and `UserName` properties
UserId = (int)o.XPathSelectElement("CustomFields/CustomField")
})
.ToList()
};
Try a single query
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Request request = doc.Elements("EnvelopeStatus").Select(x => new Request() {
Id = (string)x.Element("Id"),
Status = (string)x.Element("Status"),
Url = (string)x.Descendants("CustomField").Where(y => (string)y.Descendants("Name").FirstOrDefault() == "Url").Select(z => z.Element("Value")).FirstOrDefault(),
ItemId = (int)x.Descendants("CustomField").Where(y => (string)y.Descendants("Name").FirstOrDefault() == "ItemId").Select(z => z.Element("Value")).FirstOrDefault(),
List = (string)x.Descendants("CustomField").Where(y => (string)y.Descendants("Name").FirstOrDefault() == "List").Select(z => z.Element("Value")).FirstOrDefault(),
Signers = x.Descendants("RecipientStatus").Select(y => new Signer() {
Type = (string)y.Descendants("Type").FirstOrDefault(),
Email = (string)y.Descendants("Email").FirstOrDefault(),
UserName = (string)y.Descendants("UserName").FirstOrDefault(),
UserId = (int)y.Descendants("CustomField").FirstOrDefault()
}).ToList()
}).FirstOrDefault();
}
}
public class Request
{
public string Id { get; set; }
public string Status { get; set; }
public string Url { get; set; }
public string List { get; set; }
public int ItemId { get; set; }
public List<Signer> Signers { get; set; }
}
public class Signer
{
public string Type { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
}
}

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