Specific XML Attribute values into Class List - c#

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.

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).

loading a large xml file and comparing data

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

Parsing this xml

I tried a lot of codes but nothing worked.
I have XML:
<books>
<book>
<title>first title</title>
<publisher>first publisher</publisher>
<description>first description</description>
<published>1410</published>
</book>
<book>
<title>second book</title>
<publisher>second publisher</publisher>
<description>second description</description>
<published>1914</published>
</book>
[another book]
[another book2]
</books>
And I want input like this:
first title | first publisher | first description | 1410
second title | second publisher | second descirpion | 1914
[another books]
"My" Code:
var xdoc = XDocument.Load(#"5.xml");
var entries = from e in xdoc.Descendants("book")
select new
{
Title = (string)e.Element("title"),
Description = (string)e.Element("description")
};
//I DON'T KNOW WHAT IT DO, I FOUND THIS BUT I DON'T KNOW WHAT NEXT
I can parse first book but i can't parse multiple. Sorry for language.
If you want to use an XDocument you may try the following:
using System;
using System.Xml.Linq;
class Program
{
static void Main()
{
var doc = XDocument.Load("5.xml");
var books = doc.Descendants("book");
foreach (var book in books)
{
string title = book.Element("title").Value;
string publisher = book.Element("publisher").Value;
string description = book.Element("description").Value;
string published = book.Element("published").Value;
Console.WriteLine("{0}\t{1}\t{2}\t{3}", title, publisher, description, published);
}
}
}
If on the other hand the XML you are trying to parse is very big and cannot fit into memory it is better to use an XmlReader which will allow you to process it record by record:
using System;
using System.Xml;
class Program
{
static void Main()
{
using (var reader = XmlReader.Create("5.xml"))
{
string title = null, publisher = null, description = null, published = null;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "book")
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}", title, publisher, description, published);
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "title")
{
title = reader.ReadInnerXml();
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "publisher")
{
publisher = reader.ReadInnerXml();
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "description")
{
description = reader.ReadInnerXml();
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "published")
{
published = reader.ReadInnerXml();
}
}
}
}
}
With this approach you can deal with arbitrary large XML files.
You can use this code to parse your XML
XDocument xDoc = XDocument.Load("5.xml");
var books = (from b in xDoc.Descendants("book")
select new
{
title = (string) b.Element("title"),
publisher = (string) b.Element("publisher"),
despription = (string) b.Element("description"),
published = (string) b.Element("published")
}).ToList();
foreach (var book in books)
{
Console.WriteLine("{0} | {1} | {2} |{3}",book.title,book.publisher,book.despription,book.published);
}

How to get value from a specific child element in XML using XmlReader?

Here's the XML string.
<?xml version="1.0" encoding="utf-16"?>
<questionresponses>
<question id="dd7e3bce-57ee-497a-afe8-e3d8d25e2671">
<text>Question 1?</text>
<response>abcdefg</response>
<correctresponse>123</correctresponse>
</question>
<question id="efc43b1d-048f-4ba9-9cc0-1cc09a7eeaf2">
<text>Question 2?</text>
<response>12345678</response>
<correctresponse>123</correctresponse>
</question>
</questionresponses>
So how could I get value of <response> element by given question Id? Say, if I give id value = "dd7e3bce-57ee-497a-afe8-e3d8d25e2671", I'd like to have string value abcdefg returned as result.
var xmlstr = "content from above xml example";
using (var reader = XmlReader.Create(new StringReader(xmlstr)))
{
while(reader.Read())
{
if(reader.IsStartElement())
{
var attr = reader["id"];
if(attr != null && attr == "dd7e3bce-57ee-497a-afe8-e3d8d25e2671")
{
if(reader.ReadToDescendant("response"))
{
result = reader.Value; // <= getting empty string? So what's wrong?
break;
}
}
}
}
}
you might need to do like this , problem i think is reader is not moving to text and because of that you are getting empty
if(reader.ReadToDescendant("response"))
{
reader.Read();//this moves reader to next node which is text
result = reader.Value; //this might give value than
break;
}
Above one is working for me you can try out at your end
I would use LINQ2XML..
XDocument doc=XDocument.Parse(xmlstr);
String response=doc.Elements("question")
.Where(x=>x.Attribute("id")==id)
.Single()
.Element("response")
.Value;
if (reader.NodeType == XmlNodeType.Element)
{
if(reader.Name == "response")
{
reader.read();
var res = reader.Value;
}
}
//it works for me !!!!
You can use this function to get a response for specific questions from XML stored in QuestionXML.xml.
private string getResponse(string questionID)
{
string response = string.Empty;
using (StreamReader sr = new StreamReader("QuestionXML.xml", true))
{
XmlDocument xmlDoc1 = new XmlDocument();
xmlDoc1.Load(sr);
XmlNodeList itemNodes = xmlDoc1.GetElementsByTagName("question");
if (itemNodes.Count > 0)
{
foreach (XmlElement node in itemNodes)
{
if (node.Attributes["id"].Value.ToString() == questionID.Trim())
{
response = node.SelectSingleNode("response").InnerText;
break;
}
}
}
}
return response;
}

XML values wont read to string

I am having difficulties with my XML code it doesn't seem to saving and when I print it out nothing happens. I am not sure what is wrong because before it would load into my listbox but it would load incorrectly. The code is below and the purpose of my XML reading is the store the values in a list and then get a selected tag and add it to a listbox.
String workingDir = Directory.GetCurrentDirectory();
XmlTextReader textReader = new XmlTextReader(workingDir + #"\XML.xml");
textReader.Read();
XmlNodeType type;
while (textReader.Read())
{
textReader.MoveToElement();
type = textReader.NodeType;
if (type == XmlNodeType.Text)
{
if (textReader.Name == "Code")
{
textReader.Read();
code = textReader.Value;
Console.WriteLine(code);
}
if (textReader.Name == "Name")
{
textReader.Read();
name = textReader.Value;
Console.WriteLine(name);
}
if (textReader.Name == "Semester")
{
textReader.Read();
semester = textReader.Value;
Console.WriteLine(semester);
}
if (textReader.Name == "Prerequisite")
{
textReader.Read();
preReq = textReader.Value;
Console.WriteLine(code);
}
if (textReader.Name == "LectureSlot")
{
textReader.Read();
lSlot = textReader.Value;
Console.WriteLine(lSlot);
}
if (textReader.Name == "TutorialSlot")
{
textReader.Read();
tSlot = textReader.Value;
Console.WriteLine(tSlot);
}
if (textReader.Name == "Info")
{
textReader.Read();
info = textReader.Value;
module.Add(new modules(name, code, semester, tSlot, lSlot, info, preReq));
}
}
foreach (object o in module)
{
modules m = (modules)o;
String hold = m.mName;
selectionBox.Items.Add(hold);
}
}
The thing is that you look for type == XmlNodeType.Text, but text nodes does not have any name, no text nodes will match textReader.Name == "Code".
You need to store textReader.Name from the last node with type == XmlNodeType.Element in a variable and use the stored name when you find the XmlNodeType.Text node.
I think the most likely reason is that in each of your if statements, you are using textReader.Read(). For most Readers this will read the next item, not the current.
As the other answer has said, you need to look at the element for the Name and then read for the value.
Consider something like this instead:
while (textReader.Read())
{
textReader.MoveToElement();
type = textReader.NodeType;
if (type == XmlNodeType.Element)
{
textReader.Read();
switch( textReader.Name )
{
case "Code":
code = textReader.Value;
break;
case "Name":
name = textReader.Value;
break;
//SNIP
case "Info":
info = textReader.Value;
module.Add(new modules(name, code, semester, tSlot, lSlot, info, preReq));
break;
default:
//Whatever you do here
break;
}
Console.WriteLine(textReader.Value);
}
foreach (object o in module)
{
modules m = (modules)o;
String hold = m.mName;
selectionBox.Items.Add(hold);
}
}
This way your XMLTextReader is only reading one node per iteration, and you have a lot fewer if checks - this is the situation a switch case was designed for.

Categories