How to check the attribute value string from linq in c# - c#

I have a string value in column of database table :-
<Attributes><ProductAttribute ID="322"><ProductAttributeValue><Value>782</Value></ProductAttributeValue></ProductAttribute></Attributes>
There are multiple column with the same format.
Now I need to check ProductAttributeValue and get the data from linQ
currently I am doing by
var id = 782
var string = "<Attributes><ProductAttribute ID="322"><ProductAttributeValue><Value>" + id + "</Value></ProductAttributeValue></ProductAttribute></Attributes>";
var value = sometable.where(x => x.valueString == string).FirstOrDefault();
Is there any way to get direct from linq?

This can be done using LINQ to XML.
using System.Linq;
using System.Xml.Linq;
...
var id = "Value To Find";
var str = "<Attributes><ProductAttribute ID=\"322\"><ProductAttributeValue><Value>" + id + "</Value></ProductAttributeValue></ProductAttribute></Attributes>";
var xml = XDocument.Parse(str);
var val = xml
.Element("Attributes")
.Element("ProductAttribute")
.Element("ProductAttributeValue")
.Element("Value")?.Value;
Since there is only 1 of each element in the xml data structure you can use Element, if there are multiple you can use Elements and operate on them as a collection.
You can filter elements like usual using Where and other extension methods.
var valToFind = "722";
var val = xml
.Element("Attributes")
.Elements("ProductAttribute")
.Where(node => node
.Element("ProductAttributeValue")
?.Element("Value")
?.Value == valToFind
)
.FirstOrDefault();
The above will find the ProductAttribute node that has a ProductAttributeValue Value equal to the valToFind. valToFind is a string for quick comparison against the xml string value.

Related

How to Read and Write to an XML file using XDocument and XElements? [duplicate]

I'm using LINQ together with XDocument to read a XML File. This is the code:
XDocument xml = XDocument.Load(filename);
var q = from b in xml.Descendants("product")
select new
{
name = b.Element("name").Value,
price = b.Element("price").Value,
extra = b.Element("extra1").Value,
deeplink = b.Element("deepLink").Value
};
Now the problem is, the extra1 field is not always present. There are items in the XML file without that node. If that happens it's crashing with a NullReferenceException.
Is there any possibility to include a "check if null" so I can prevent it from crashing?
Use (string) instead of .Value:
var q = from b in xml.Descendants("product")
select new
{
name = (string)b.Element("name"),
price = (double?)b.Element("price"),
extra = (string)b.Element("extra1"),
deeplink = (string)b.Element("deepLink")
};
This also works with other datatypes, including many nullable types in case the element is not always present.
You can use "null coalescing" operator:
var q = from b in xml.Descendants("product")
select new
{
name = (string)b.Element("name") ?? "Default Name",
price = (double?)b.Element("price") ?? 0.0,
extra = (string)b.Element("extra1") ?? String.Empty,
deeplink = (string)b.Element("deepLink") ?? String.Empty
};
This way, you will have full control about default value used when there's no element.
Use the following example for checking existence of any element before using that element.
if( b.Elements("extra1").Any() )
{
extra = b.Element("extra1").Value;
}
Here is sample example to read XML file using XDocument.
XDocument objBooksXML = XDocument.Load(Server.MapPath("books.xml"));
var objBooks = from book in
objBooksXML.Descendants("Book")
select new {
Title = book.Element("Title").Value,
Pages = book.Element("Pages").Value
};
Response.Write(String.Format("Total {0} books.", objBooks.Count()));
gvBooks.DataSource = objBooks;
gvBooks.DataBind();

Sort List by date values

I have the following list -
List<string> finalMessageContent
where
finalMessageContent[0] = "<div class="mHr" id="mFID">
<div id="postedDate">11/12/2015 11:12:16</div>
</div>" // etc etc
I am trying to sort the list by a particular value located in the entires - postedDate tag.
Firstly I have create an new object and then serialized it to make the html elements able to be parsed -
string[][] newfinalMessageContent = finalMessageContent.Select(x => new string[] { x }).ToArray();
string json = JsonConvert.SerializeObject(newfinalMessageContent);
JArray markerData = JArray.Parse(json);
And then used Linq to try and sort using OrderByDescending -
var items = markerData.OrderByDescending(x => x["postedDate"].ToString()).ToList();
However this is failing when trying to parse the entry with -
Accessed JArray values with invalid key value: "postedDate". Array position index expected.
Perhaps linq is not the way to go here however it seemed like the most optimised, where am I going wrong?
First, i would not use string methods, regex or a JSON-parser to parse HTML. I would use HtmlAgilityPack. Then you could provide such a method:
private static DateTime? ExtractPostedDate(string inputHtml, string controlID = "postedDate")
{
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(inputHtml);
HtmlNode div = doc.GetElementbyId(controlID);
DateTime? result = null;
DateTime value;
if (div != null && DateTime.TryParse(div.InnerText.Trim(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out value))
result = value;
return result;
}
and following LINQ query:
finalMessageContent = finalMessageContent
.Select(s => new { String = s, Date = ExtractPostedDate(s) })
.Where(x => x.Date.HasValue)
.OrderByDescending(x => x.Date.Value)
.Select(x => x.String)
.ToList();
Don't know if I get your question right.
But did you know that you can parse HTML with XPath?
foreach (var row in doc.DocumentNode.SelectNodes("//div[#id="postedDate"]"))
{
Console.WriteLine(row.InnerText);
}
this is just an example from the top of my head you might have to double-check the XPath query depending on your document. You can also consider converting it to array or parsing the date and do other transformations with it.
Like I said this is just from the top of my head. Or if the html is not so compley consider to extract the dates with an RegEx but this would be a topic for another question.
HTH
Json Serializer serializes JSON typed strings. Example here to json
To parse HTML I suggest using HtmlAgility https://htmlagilitypack.codeplex.com/
Like this:
HtmlAgilityPack.HtmlDocument htmlparsed = new HtmlAgilityPack.HtmlDocument();
htmlParsed.LoadHtml(finalMessageContent[0]);
List<HtmlNode> OrderedDivs = htmlParsed.DocumentNode.Descendants("div").
Where(a => a.Attributes.Any(af => af.Value == "postedDate")).
OrderByDescending(d => DateTime.Parse(d.InnerText)); //unsafe parsing

How to parse nested elements using LINQ to XML

I have an XML file with multiple checkItem elements. I need to save each checkItem element into a database. I'm having a difficult time getting exactly what I need using the query below.
<checkItem>
<checkItemType>check</checkItemType>
<checkAmount>195000</checkAmount>
<nonMICRCheckData>
<legalAmount>195000</legalAmount>
<issueDate>2010-04-30</issueDate>
<other>PAY VAL 20 CHARACTER</other>
</nonMICRCheckData>
<postingInfo>
<date>2013-05-01</date>
<RT>10108929</RT>
<accountNumber>111111111</accountNumber>
<seqNum>11111111</seqNum>
<trancode>111111</trancode>
<amount>195000</amount>
<serialNumber>1111111</serialNumber>
</postingInfo>
<totalImageViewsDelivered>2</totalImageViewsDelivered>
<imageView>
<imageIndicator>Actual Item Image Present</imageIndicator>
<imageViewInfo>
<Format>
<Baseline>TIF</Baseline>
</Format>
<Compression>
<Baseline>CCITT</Baseline>
</Compression>
<ViewSide>Front</ViewSide>
<imageViewLocator>
<imageRefKey>201305010090085000316000085703_Front.TIF</imageRefKey>
<imageFileLocator>IFTDISB20130625132900M041.zip</imageFileLocator>
</imageViewLocator>
</imageViewInfo>
<imageViewInfo>
<Format>
<Baseline>TIF</Baseline>
</Format>
<Compression>
<Baseline>CCITT</Baseline>
</Compression>
<ViewSide>Rear</ViewSide>
<imageViewLocator>
<imageRefKey>201305010090085000316000085703_Rear.TIF</imageRefKey>
<imageFileLocator>IFTDISB20130625132900M041.zip</imageFileLocator>
</imageViewLocator>
</imageViewInfo>
</imageView>
</checkItem>
Here is the query I've been working with. I've tried several different ways with no luck. Without the use of .Concat, I cannot get the other elements; however, using .Concat does not allow me to get all values in a manageable format. I need to separate the Front and Rear imageViews based on the ViewSide value, and only need the imageRefKey and imageFileLocator values from the imageView element. Can anyone point me in the right direction?
var query = doc.Descendants("checkItem")
//.Concat(doc.Descendants("postingInfo"))
//.Concat(doc.Descendants("imageViewLocator"))//.Where(x => (string)x.Element("ViewSide") == "Front"))
//.Concat(doc.Descendants("imageViewInfo").Where(x => (string)x.Element("ViewSide") == "Rear"))
.Select(x => new {
CheckAmount = (string) x.Element("checkAmount"),
ImageRefKey = (string) x.Element("imageRefKey"),
PostingDate = (string) x.Element("dare"),
//FrontViewSide = (string) x.Element("ViewSide"),
//RearViewSide = (string) x.Element("BViewSide")
});
You can easily get nested elements of any XElement by just calling the Elements() method of that instance, then calling Select() on that collection, to created a nested collection of an anonymous type in your main anonymous type.
var query = doc.Elements("checkItem")
.Select( x =>
new
{
CheckAmount = (string) x.Element("checkAmount"),
ImageRefKey = (string) x.Element("imageRefKey"),
PostingDate = (string) x.Element("dare"),
ImageViews = x.Element("ImageView").Elements("ImageViewInfo")
.Select(iv=>
new
{
Format = iv.Element("Format").Element("Baseline").Value
// more parsing here
}
}

How to write XPath expression to select node name from its value

I'm trying to write an XPath expression to select the name of a node from its value in "qualities" and then select in "qualityNames" the value inside node whose name has previously captured.
E.g. In "qualities" - got value "4", take name "rarity3" then in "qualityNames" I got node named "rarity3" and take value "amazingrarity"
<result>
<status>1</status>
<qualities>
<Normal>0</Normal>
<rarity1>1</rarity1>
<rarity2>2</rarity2>
<vintage>3</vintage>
<rarity3>4</rarity3>
<rarity4>5</rarity4>
</qualities>
<qualityNames>
<Normal>Normal</Normal>
<rarity1>Genuine</rarity1>
<rarity2>rarity2</rarity2>
<vintage>Vintage</vintage>
<rarity3>amazingrarity</rarity3>
<rarity4>Unusual</rarity4>
</qualityNames>
</result>
I'm doing this in C# (It's a MVC App) and I'd prefer to use XPath because I'm indexing the XML and I haven't found a fastest way to query in-memory technique (this XML file has ~3MB and I'm using IndexingXPathNavigator).
Use the local-name() and text() functions + predicates. For value "4" it will be
//qualityNames/*[local-name()=local-name(//qualities/*[text() = '4'])]
Tested with http://www.xpathtester.com
Sounds like you want to create a dictionary of key/value pairs (assuming the node names are only needed to find matches and aren't important to your code).
If so, you can use the following:
var doc = XElement.Parse(#"<result>
<status>1</status>
<qualities>
<Normal>0</Normal>
<rarity1>1</rarity1>
<rarity2>2</rarity2>
<vintage>3</vintage>
<rarity3>4</rarity3>
<rarity4>5</rarity4>
</qualities>
<qualityNames>
<Normal>Normal</Normal>
<rarity1>Genuine</rarity1>
<rarity2>rarity2</rarity2>
<vintage>Vintage</vintage>
<rarity3>amazingrarity</rarity3>
<rarity4>Unusual</rarity4>
</qualityNames>
</result>");
var query = from quality in doc.XPathSelectElements("qualities/*")
join qualityName in doc.XPathSelectElements("qualityNames/*")
on quality.Name equals qualityName.Name
select new { Key = quality.Value, Value = qualityName.Value };
var qualities = query.ToDictionary(a => a.Key, a => a.Value);
var quality3 = qualities["3"];
// quality3 == "Vintage"
var quality4 = qualities["4"];
// quality4 == "amazingrarity"
EDIT: example of how to cache this dictionary
// add reference to System.Web dll
public Dictionary<string, string> GetQualities()
{
// assuming this code is in a controller
var qualities = this.HttpContext.Cache["qualities"] as Dictionary<string, string>;
if (qualities == null)
{
// LoadQualitiesFromXml() is the code above
qualities = LoadQualitiesFromXml();
this.HttpContext.Cache["qualities"] = qualities;
}
return qualities;
}
I think this is what you asked
var rarity3ValueInQualities = xml.SelectSingleNode("/result/qualities/rarity3").InnerText;
var rarity3ValueInqualityNames = xml.SelectSingleNode("/result/qualityNames/rarity3").InnerText;

how to get count of the repeated values xml element in wpf

In above figure the element Chandru is repeated as two times.
so i have to count the repeated element.
But i don't know to get count of repeated element.
please help me.
Here the code what i wrote
public XML_3()
{
this.InitializeComponent();
XmlDocument doc = new XmlDocument();
doc.Load("D:/student_2.xml");
XmlNodeList student_list = doc.GetElementsByTagName("Student");
foreach (XmlNode node in student_list)
{
XmlElement student = (XmlElement)node;
string sname = student.GetElementsByTagName("Chandru")[0].InnerText;
string fname = student.GetElementsByTagName("FName")[0].InnerText;
string id = student.GetElementsByTagName("Chandru")[0].Attributes["ID"].InnerText;
Window.Content = sname + fname + id;
}
}
var count = student.GetElementsByTagName("Chandru").Count;
I think it would be easier using LINQ to XML and X-Classes:
var doc = XDocument.Load("D:/student_2.xml");
var results = (from s in doc.Root.Elements()
group s by s.Name into g
select new { Name = g.Key, Count = g.Count() }).ToList();
this will give you a list of elements with two properties each: Name and Count. If you want to receive only that names, which occurred more than once you can add where g.Count() > 1 between group and select statement.
I am not a C# programmer, but hence, I can give you the algorithm then you can try to apply it on your program.
Define an array of struct, the struct must have two fields: ElmntName and NbOccurance.
struct Elmnt
{ String ElmntName;
Int NbOccurance;
} MyElement;
Then each element you pass thorugh, pass through your array, if the elementwas not found, save it as new element with NbOccurance=0;
Else, if it was found increment the number of occursnces.
At the end of reading your xml, you will get a list containing the names of the elements and their occurances.

Categories