loading a large xml file and comparing data - c#

i have a large xml file and i want to search through the Desc nodes in it to see if any match the text a user inputs then if it does get the data from the code node directly underneath the Desc node this is an example of the xml im using except mine has around 1000 Script nodes in it
<Scripts>
<script>
<Name>mst</Name>
<Desc>Destroy 1 spell/trap on the field</Desc>
<code>function c????????.initial_effect(c)
--Activate
local e1=Effect.CreateEffect(c)
e1:SetCategory(CATEGORY_DESTROY)
e1:SetType(EFFECT_TYPE_ACTIVATE)
e1:SetCode(EVENT_FREE_CHAIN)
e1:SetHintTiming(0,TIMING_END_PHASE+TIMING_EQUIP)
e1:SetProperty(EFFECT_FLAG_CARD_TARGET)
e1:SetTarget(c????????.target)
e1:SetOperation(c????????.activate)
c:RegisterEffect(e1)
end
function c????????.filter(c)
return c:IsDestructable() and c:IsType(TYPE_SPELL+TYPE_TRAP)
end
function c????????.target(e,tp,eg,ep,ev,re,r,rp,chk,chkc)
if chkc then return chkc:IsOnField() and c????????.filter(chkc) end
if chk==0 then return Duel.IsExistingTarget(c????????.filter,tp,LOCATION_ONFIELD,LOCATION_ONFIELD,1,e:GetHandler()) end
Duel.Hint(HINT_SELECTMSG,tp,HINTMSG_DESTROY)
local g=Duel.SelectTarget(tp,c????????.filter,tp,LOCATION_ONFIELD,LOCATION_ONFIELD,1,1,e:GetHandler())
Duel.SetOperationInfo(0,CATEGORY_DESTROY,g,1,0,0)
end
function c????????.activate(e,tp,eg,ep,ev,re,r,rp)
local tc=Duel.GetFirstTarget()
if tc:IsRelateToEffect(e) then
Duel.Destroy(tc,REASON_EFFECT)
end
end</code>
</script>
</Scripts>
this is the code i found online but it doesnt seem to work
public static string Xmlload(String node)
{
XmlTextReader script = new XmlTextReader("Scripts.xml");
while (script.Read())
{
if (script.NodeType == XmlNodeType.Element &&
script.LocalName == node &&
script.IsStartElement() == true)
{
ProcessRewardNode(script,node);
script.Skip();
return myID;
}
else
{
return null;
}
}
myID = "test";
return myID;
}
private static void ProcessRewardNode(XmlTextReader RewardReader, string node)
{
XmlDocument RewardXmlDoc = new XmlDocument();
RewardXmlDoc.LoadXml(RewardReader.ReadOuterXml());
// we can use xpath as below
myID = RewardXmlDoc.SelectSingleNode(node).InnerText;
}
public static string myID { get; set; }
hope someone can help thanks

Please try the following. I added using to properly dispose of the XmlTextReader after use.
void Main()
{
Console.WriteLine(Xmlload("Destroy 1 spell/trap on the field"));
}
public static string Xmlload(String textUserInputs)
{
using (var script = new XmlTextReader("Scripts.xml"))
{
while (script.Read())
{
if (script.NodeType == XmlNodeType.Element &&
script.LocalName == "Desc" &&
script.IsStartElement() == true)
{
var desc = script.ReadElementContentAsString();
if (desc == textUserInputs)
{
script.ReadToNextSibling("code");
return script.ReadElementContentAsString();
}
}
}
}
return null;
}
References
XmlTextReader Class

Related

Best way to persist TreeView tag that has an object attached to it

I've created a TreeView, in which some of the nodes have the Tag property set as an object Machine which contains an IP address, FQDN and a friendly name as strings
I can persist the TreeView, keeping the structure of the tree when reloading the program using XML. However, the Tag value is just Project.Machine and obviously doesn't contain the data.
The program works by asking the user to input the data into textboxes, which are then created into the machine object, and then tied to the selected nodes Tag property.
Should I serealize the class and then add the Tag property to the node when the program initiates?
Here's the class to persist the TreeView
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace PRTG_Ripoff
{
internal class TreeViewSerializer
{
// Constants
// Xml tag for node
private const string XmlNodeTag = "node";
// Xml attributes for node
private const string XmlNodeTextAtt = "text";
private const string XmlNodeTagAtt = "tag";
private const string XmlNodeImageIndexAtt = "imageindex";
public void DeserializeTreeView(TreeView tv, string filename)
{
XmlTextReader reader = null;
try
{
// disable re-drawing of tree view till nodes are added
tv.BeginUpdate();
reader = new XmlTextReader(filename);
TreeNode parentNode = null;
while(reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == XmlNodeTag)
{
TreeNode newNode = new TreeNode();
bool isEmptyElement = reader.IsEmptyElement;
// loading node attributes
int attributeCount = reader.AttributeCount;
if (attributeCount > 0)
{
for (int i = 0; i < attributeCount; i++)
{
reader.MoveToAttribute(i);
SetAttributeValue(newNode,
reader.Name, reader.Value);
}
}
// add new node to Parent Node or TreeView
if (parentNode != null)
parentNode.Nodes.Add(newNode);
else
tv.Nodes.Add(newNode);
// making current node 'ParentNode' if its not empty
if (!isEmptyElement)
{
parentNode = newNode;
}
}
}
// moving up to in TreeView if end tag is encountered
else if (reader.NodeType == XmlNodeType.EndElement)
{
if (reader.Name == XmlNodeTag)
{
parentNode = parentNode.Parent;
}
}
else if (reader.NodeType == XmlNodeType.XmlDeclaration)
{
//Ignore Xml Declaration
}
else if (reader.NodeType == XmlNodeType.None)
{
return;
}
else if (reader.NodeType == XmlNodeType.Text)
{
parentNode.Nodes.Add(reader.Value);
}
}
}
finally
{
// enabling redrwaing of treeview
tv.EndUpdate();
reader.Close();
}
}
/// Used by Deserialize method for setting properties of
/// TreeNode from xml node attributes
private void SetAttributeValue(TreeNode node,
string propertyName, string value)
{
if (propertyName == XmlNodeTextAtt)
{
node.Text = value;
}
else if (propertyName == XmlNodeImageIndexAtt)
{
node.ImageIndex = int.Parse(value);
}
else if (propertyName == XmlNodeTagAtt)
{
node.Tag = value;
}
}
public void SerializeTreeView(TreeView treeView, string fileName)
{
XmlTextWriter textWriter = new XmlTextWriter(fileName,
System.Text.Encoding.ASCII);
// writing the xml declaration tag
textWriter.WriteStartDocument();
//textWriter.WriteRaw("\r\n");
// writing the main tag that encloses all node tags
textWriter.WriteStartElement("TreeView");
// save the nodes, recursive method
SaveNodes(treeView.Nodes, textWriter);
textWriter.WriteEndElement();
textWriter.Close();
}
private void SaveNodes(TreeNodeCollection nodesCollection, XmlTextWriter textWriter)
{
for (int i = 0; i < nodesCollection.Count; i++)
{
TreeNode node = nodesCollection[i];
textWriter.WriteStartElement(XmlNodeTag);
textWriter.WriteAttributeString(XmlNodeTextAtt,
node.Text);
textWriter.WriteAttributeString(
XmlNodeImageIndexAtt, node.ImageIndex.ToString());
if (node.Tag != null)
textWriter.WriteAttributeString(XmlNodeTagAtt,
node.Tag.ToString());
// add other node properties to serialize here
if (node.Nodes.Count > 0)
{
SaveNodes(node.Nodes, textWriter);
}
textWriter.WriteEndElement();
}
}
}
}
And how I'm creating the object
private void Details_Btn_Click(object sender, EventArgs e)
{
// If the text boxes aren't empty
if (FQDN_Txt.Text != "" && IP_Txt.Text != "" && Name_Txt.Text != "")
{
if(mainTree.SelectedNode == null)
{
// If no node is selected
MessageBox.Show("Select a node!");
} else
{
// Create new machine object using data from the text boxes
Machine machine = new Machine(FQDN_Txt.Text, Name_Txt.Text, IP_Txt.Text);
// Set the tag as the machine object
mainTree.SelectedNode.Tag = machine;
}
} else
{
MessageBox.Show("these are empty mate");
}
}
And how I'm retrieving the data from the Tag property
private void mainTree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
mainTree.SelectedNode = e.Node;
TreeNode tn = mainTree.SelectedNode;
infoPanel.Visible = true;
MachineName_lbl.Text = tn.Text;
if (mainTree.SelectedNode.Tag != null)
{
IP_Lbl.Text = ((Machine)mainTree.SelectedNode.Tag).MachineIP;
}
}
As you don't currently use the Machine object instance anywhere else, then rather than create a complete instance and save it in Tag you could instead just create either a Tuple or KeyValuePair and save that in the Tag.
Then you can easily Serialise these values when you write your nodes in SaveNodes. (and then read it again and create and add the Tuple / KeyValuePair).
If you later need to use the Machine object, then you could just seralise the key and then do a look-up from your collection of Machine. Either do this when you need it, or at the time you create your Treeview Nodes (and asign it to Tag).

Efficient Parsing of XML

Good day,
I'm writing a program in C# .Net to manage products of my store,
Following a given link I can retrieve an XML file that contains all the possible products that I can list onto my storefront.
The XML structure looks like this :
<Product StockCode="103-10440">
<lastUpdated><![CDATA[Fri, 20 May 2016 17:00:03 GMT]]></lastUpdated>
<StockCode><![CDATA[103-10440]]></StockCode>
<Brand><![CDATA[3COM]]></Brand>
<BrandID><![CDATA[14]]></BrandID>
<ProdName><![CDATA[BIG FLOW BLOWING JUNCTION FLEX BLOCK, TAKES 32, 40]]> </ProdName>
<ProdDesc/>
<Categories>
<TopCat><![CDATA[Accessories]]></TopCat>
<TopCatID><![CDATA[24]]></TopCatID>
</Categories>
<ProdImg/>
<ProdPriceExclVAT><![CDATA[30296.79]]></ProdPriceExclVAT>
<ProdQty><![CDATA[0]]></ProdQty>
<ProdExternalURL><![CDATA[http://pinnacle.eliance.co.za/#!/product/4862]]></ProdExternalURL>
</Product>
Here are the entries I'm looking for :
lastUpdated
StockCode
Brand
ProdName
ProdDesc
TopCat <--- nested in Categories tag.
ProdImg
ProdPriceExclVAT
ProdQty
ProdExternalURL
This is all fine to handle , and in-fact I did :
public ProductList Parse() {
XmlDocument doc = new XmlDocument();
doc.Load(XMLLink);
XmlNodeList ProductNodeList = doc.GetElementsByTagName("Product");
foreach (XmlNode node in ProductNodeList) {
Product Product = new Product();
for (int i = 0; i < node.ChildNodes.Count; i++) {
if (node.ChildNodes[i].Name == "StockCode") {
Product.VariantSKU = Convert.ToString(node.ChildNodes[i].InnerText);
}
if (node.ChildNodes[i].Name == "Brand") {
Product.Vendor = Convert.ToString(node.ChildNodes[i].InnerText);
}
if (node.ChildNodes[i].Name == "ProdName") {
Product.Title = Convert.ToString(node.ChildNodes[i].InnerText);
Product.SEOTitle = Product.Title;
Product.Handle = Product.Title;
}
if (node.ChildNodes[i].Name == "ProdDesc") {
Product.Body = Convert.ToString(node.ChildNodes[i].InnerText);
Product.SEODescription = Product.Body;
if (Product.Body == "") {
Product.Body = "ERROR";
Product.SEODescription = "ERROR";
}
}
if (node.ChildNodes[i].Name == "Categories") {
if (!tempList.Categories.Contains(node.ChildNodes[i].ChildNodes[0].InnerText)) {
if (!tempList.Categories.Contains("All")) {
tempList.Categories.Add("All");
}
tempList.Categories.Add(node.ChildNodes[i].ChildNodes[0].InnerText);
}
Product.Type = Convert.ToString(node.ChildNodes[i].ChildNodes[0].InnerText);
}
if (node.ChildNodes[i].Name == "ProdImg") {
Product.ImageSrc = Convert.ToString(node.ChildNodes[i].InnerText);
if (Product.ImageSrc == "") {
Product.ImageSrc = "ERROR";
}
Product.ImageAlt = Product.Title;
}
if (node.ChildNodes[i].Name == "ProdPriceExclVAT") {
float baseprice = float.Parse(node.ChildNodes[i].InnerText);
double Costprice = ((baseprice * 0.14) + (baseprice * 0.15) + baseprice);
Product.VariantPrice = Costprice.ToString("0.##");
}
}
Product.Supplier = "Pinnacle";
if (!tempList.Suppliers.Contains(Product.Supplier)) {
tempList.Suppliers.Add(Product.Supplier);
}
tempList.Products.Add(Product);
}
return tempList;
}
}
The problem is however, that this way of doing it, takes about 10 seconds to finish, and this is only just the first of multiple such files that I have to parse.
I am looking for the most efficient way to parse this XML file, getting all the fields's data that I mentioned above.
EDIT :
I benchmarked the code when running with a pre-downloaded copy of the file, and when downloading the file from the server at runtime :
With local copy : 5 Seconds.
Without local copy : 7.30 Seconds.
With large XML files you have to use an XmlReader. The code below will read one Product at a time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create("filename");
while(!reader.EOF)
{
if (reader.Name != "Product")
{
reader.ReadToFollowing("Product");
}
if (!reader.EOF)
{
XElement product = (XElement)XElement.ReadFrom(reader);
string lastUpdated = (string)product.Element("lastUpdated");
}
}
}
}
}

How I can filter a Xml File with a Attribute?

hi I want to control a xml file... for this i use linq to xml.
private string GetGroup(string xml, string id)
{
XDocument document;
XElement element;
try
{
document = XDocument.Load(xml);
//element = document.Root.Elements("Permissiongroup").FirstOrDefault(e => e.Element("id").Value == id);
element = document.Elements("Permissiongroup").FirstOrDefault(e => e.Element("id").Value == id);
if (element != null)
{
return element.Element("display").Value;
}
else
{
return string.Empty;
}
}
catch (Exception)
{
return null;
}
finally
{
document = null;
element = null;
}
}
here is my xml:
<?xml version="1.0" encoding="iso-8859-1"?>
<Permissiongroup>
<Permission id="Hessen" display="KV-IT" />
<Permission id="Berlin" display="DBG_Update" />
</Permissiongroup>
For example i want if the method is ..
string group = GetGroup(xmlpath, "Hessen");
group is "KV-IT"
there are a few things wrong with what you currently have - you're missing Permission from the query and looking for an element instead of an attribute. The following works, albeit I would split it down to check for the existence of elements (e.g. make sure there is a Permission element, etc.) rather than relying on error handling.
// string group = GetGroup(xmlpath, "Hessen"); // returns KV-IT
// string group2 = GetGroup(xmlpath, "Berlin"); //DBG_Update
private string GetGroup(string xml, string id)
{
XDocument document;
XElement element;
try
{
document = XDocument.Load(xml);
element = document.Elements("Permissiongroup").Elements(("Permission")).FirstOrDefault(t => t.Attribute("id").Value == id);
if (element != null)
{
return element.Attribute("display").Value;
}
else
{
return string.Empty;
}
}
catch (Exception ex)
{
return null;
}
finally
{
document = null;
element = null;
}
}
Use method Attribute() instead of using Element() to access attributes
private string GetGroup(string xml, string id)
{
XDocument document;
XElement element;
try
{
document = XDocument.Load(xml);
//element = document.Root.Elements("Permissiongroup").FirstOrDefault(e => e.Attribute("id").Value == id);
element = document.Elements("Permissiongroup").FirstOrDefault(e => e.Attribute("id").Value == id);
if (element != null)
{
return element.Attribute("display").Value;
}
else
{
return string.Empty;
}
}
catch (Exception)
{
return null;
}
finally
{
document = null;
element = null;
}
}
You can write your xml structure and then can convert xml in to xsd using
http://www.freeformatter.com/xsd-generator.html#ad-output.
Once you have xsd file , you can download jaxb ,which will convert xsd file in to POJO file
and then in your program you can access attributes of an xml like this
JAXBContext jc2 = JAXBContext.newInstance(someclassname.class);
File xml2 = new File(xml_File);
Unmarshaller unmarshaller2 = jc2.createUnmarshaller();
someclassnameObject= (someclassname) unmarshaller2.unmarshal(xml2);
and can use object to use its attributes e.g someclassnameObject.attribute

Specific XML Attribute values into Class List

Its been a while since I last tried to program and has never worked with XML before. I have a internal website that display XML
<Source>
<AllowsDuplicateFileNames>YES</AllowsDuplicateFileNames>
<Description>The main users ....</Description>
<ExportSWF>FALSE</ExportSWF>
<HasDefaultPublishDir>NO</HasDefaultPublishDir>
<Id>28577db1-956c-41f6-b775-a278c39e20a1</Id>
<IsAssociated>YES</IsAssociated>
<LogoURL>http://servername:8080/logos/9V0.png</LogoURL>
<Name>Portal1</Name>
<RequiredParameters>
<RequiredParameter>
<Id>user_name</Id>
<Name>UserID</Name>
<PlaceHolder>username</PlaceHolder>
<ShowAsDescription>true</ShowAsDescription>
</RequiredParameter>
</RequiredParameters>
I don't want the values in the child tags, there is time where there will be more than one portal thus the need/want to use a list. I only need the values inside of the Name and ID tags. also if there is a blank ID tag I don't want to store the either one of them.
My current approach to this is not working as expected:
String URLString = "http://servername:8080/roambi/SourceManager";
XmlTextReader reader = new XmlTextReader(URLString);
List<Portal> lPortals = new List<Portal>();
String sPortal = "";
String sId = "";
while (reader.Read())
{
//Get Portal ID
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Id")
{
reader.Read();
if (reader.NodeType == XmlNodeType.Text)
{
sId = reader.Value;
}
}
//Get Portal Name
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
{
reader.Read();
if (reader.NodeType == XmlNodeType.Text)
{
sPortal = reader.Value;
}
//Fill Portal List with Name and ID
if (sId != "" && sPortal != "")
{
lPortals.Add(new Portal
{
Portalname = sPortal,
Portalid = sId
});
}
}
}
foreach (Portal i in lPortals)
{
Console.WriteLine(i.Portalname + " " + i.Portalid);
}
See my standard class
class Portal
{
private String portalname;
private String portalid;
public String Portalname
{
get { return portalname; }
set { portalname = value; }
}
public String Portalid
{
get { return portalid; }
set { portalid = value; }
}
}
Please give me some advice and point me into a direction, As I said its been a while since I last programmed. My current Output is as follow:
Portal1 28577db1-956c-41f6-b775-a278c39e20a1
UserID user_name
UserID is in a child node and I do not want to display child nodes
It's much easier with XDocument class:
String URLString = "http://servername:8080/roambi/SourceManager";
XmlTextReader reader = new XmlTextReader(URLString);
XDocument doc = XDocument.Load(reader);
// assuming there's some root-node whose children are Source nodes
var portals = doc.Root
.Elements("Source")
.Select(source => new Portal
{
Portalname = (string) source.Element("Name"),
Portalid = (string) source.Element("Id")
})
.Where(p => p.Portalid != "")
.ToList();
For each <Source> node in your XML, code above will select direct children nodes (<Name> and <Id>) and build appropriate Portal instances.

How to get the contents of a HTML element using HtmlAgilityPack in C#?

I want to get the contents of an ordered list from a HTML page using HTMLAgilityPack in C#, i have tried the following code but, this is not working can anyone help, i want to pass html text and get the contents of the first ordered list found in the html
private bool isOrderedList(HtmlNode node)
{
if (node.NodeType == HtmlNodeType.Element)
{
if (node.Name.ToLower() == "ol")
return true;
else
return false;
}
else
return false;
}
public string GetOlList(string htmlText)
{
string s="";
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(htmlText);
HtmlNode nd = doc.DocumentNode;
foreach (HtmlNode node in nd.ChildNodes)
{
if (isOrderedList(node))
{
s = node.WriteContentTo();
break;
}
else if (node.HasChildNodes)
{
string sx= GetOlList(node.WriteTo());
if (sx != "")
{
s = sx;
break;
}
}
}
return s;
}
The following code worked for me
public static string GetComments(string html)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
string s = "";
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//ol"))
{
s += node.OuterHtml;
}
return s;
}
How about:
var el = (HtmlElement)doc.DocumentNode
.SelectSingleNode("//ol");
if(el!=null)
{
string s = el.OuterHtml;
}
(untested, from memory)

Categories