I'm having Trouble converting the contents of XML document to an int[] or string[]
I'm saving the x and y coordinates of 20 different picture boxes on the screen (For a jigsaw Puzzle Program) to an xml file, and are now trying to load the saved coordinates and update the jigsaw puzzle pieces to those saved locations.
Heres my code:
XmlWriter XmlWriter1;
XmlReader XmlReader1;
private void Form1_Load(object sender, EventArgs e)
{
//-------------------------------------------------
//Load Events
//-------------------------------------------------
SavedPositions = new int[40];
}
//-------------------------------------------------------
//Saves The Current Tile Locations To A Hidden XML File
//-------------------------------------------------------
public void SavePicPositionsXML()
{
using (XmlWriter1 = XmlWriter.Create("SavedPuzzle.xml"))
{
XmlWriter1.WriteStartDocument();
XmlWriter1.WriteStartElement("MTiles");
for (int i = 0; i < JigsawImgCount; i++)
{
XmlWriter1.WriteStartElement("Tile");
XmlWriter1.WriteElementString("X",Convert.ToString(MTiles[i].Pic.Location.X));
XmlWriter1.WriteElementString("Y",Convert.ToString(MTiles[i].Pic.Location.Y));
XmlWriter1.WriteEndElement();
}
XmlWriter1.WriteEndElement();
XmlWriter1.WriteEndDocument();
}
}
//---------------------------------------------------------------
//Reads Text From A Hidden Xml File & Adds It To A String Array
//---------------------------------------------------------------
private int ReadXmlFile(int Z)
{
XmlReader1 = XmlReader.Create("SavedPuzzle.xml");
XmlReader1.MoveToContent();
while (XmlReader1.Read())
{
}
// SavedPositions[B] = Convert.ToInt32(XmlReader1.Value.ToString());
return SavedPositions[Z];
}
//-------------------------------------------------
//Loads Saved Tile Positions From A Hidden Xml File
//-------------------------------------------------
private void LoadPositionsXML()
{
G = 0;
for (int i = 0; i < JigsawImgCount; i++)
{
LineX = ReadXmlFile(G);
LineY = ReadXmlFile(G + 1);
MTiles[i].Pic.Location = new Point(LineX, LineY);
G = G + 2;
}
}
What am i doing wrong???
Your ReadXmlFile method isn't doing anything really.
Consider using XmlDocument or XDocument instead of XmlWriter and XmlReader. They are a lot easier to handle.
try this:
XmlDocument document = new XmlDocument();
document.Load(#"D:/SavedPuzzle.xml");
XmlNode topNode = document.GetElementsByTagName("MTiles")[0];
foreach (XmlNode node in topNode.ChildNodes)
{
int X = Int32.Parse(node.ChildNodes[0].InnerText);
int Y = Int32.Parse(node.ChildNodes[1].InnerText);
}
The following LinqToXML statement will extract all tiles into a list in the order they are stored in the document.
I'm assuming an XML file that looks like this:
<xml>
<MTiles>
<Tile>
<X>1</X>
<Y>10</Y>
</Tile>
<Tile>
<X>2</X>
<Y>20</Y>
</Tile>
<Tile>
<X>3</X>
<Y>30</Y>
</Tile>
<Tile>
<X>4</X>
<Y>40</Y>
</Tile>
</MTiles>
</xml>
And this code will load it, and extract all the tiles into an enumerable list. Remember to put a using System.Xml.Linq at the top of the file and build against a recent enough framework (IIRC, it was introduced in .NET 3.5)
XDocument doc = XDocument.Load(/* path to the file, or use an existing reader */);
var tiles = from tile in doc.Descendants("Tile")
select new
{
X = (int)tile.Element("X"),
Y = (int)tile.Element("Y"),
};
foreach (var tile in tiles)
{
Console.WriteLine("Tile: x={0}, y={1}", tile.X, tile.Y);
}
The output from the code above using the XML file I specified is:
Tile: x=1, y=10
Tile: x=2, y=20
Tile: x=3, y=30
Tile: x=4, y=40
EDIT:
If you just want all the X-values as an array of integers, the following LINQ query would work:
int[] allXValues = (from tile in doc.Descendants("Tile")
select (int)tile.Element("X")).ToArray();
Related
I have a C# winforms application. In this application I am creating a list of CanMessage objects dynamically from an xml file.
The xml file is constructed as below
<message id="0x641" ecu="BCM" name="BODY9" dlc="8" cyclicrate="500">
<bytes>
<byte0>0x0</byte0>
<byte1>0x0</byte1>
<byte2>0x0</byte2>
<byte3>0x0</byte3>
<byte4>0x0</byte4>
<byte5>0x0</byte5>
<byte6>0x0</byte6>
<byte7>0x0</byte7>
</bytes>
The Canmessage object is constructed like this:
CanMessage(String name,String _EcuName, ulong id, ushort dlc,int[] bytearray , int cyclic)
{
this.Name = name;
this.EcuName = _EcuName;
this.Id = id;
this.Dlc = dlc;
this.Bytes = new int[dlc];
this.CyclicRate = cyclic;
int i = 0;
for(i = 0; i < dlc; i++)
{
this.Bytes[i] = bytearray[i];
}
}
Below is how I am building my Canmessage list:
public void BuildCanList()
{
try
{
XmlDocument xd = new XmlDocument();
xd.Load(XmlFile);
XmlNodeList nodelist = xd.SelectNodes("/messages/message");
foreach (XmlNode n in nodelist)
{
String name, ecuname;
ulong id;
ushort dlc;
int[] bytes = new int[Convert.ToInt32(n.Attributes.GetNamedItem("dlc").Value)];
int cyclic;
name = n.Attributes.GetNamedItem("name").Value.ToString();
id = (ulong)Convert.ToInt32(n.Attributes.GetNamedItem("id").Value, 16);
ecuname = n.Attributes.GetNamedItem("ecu").Value.ToString();
dlc = (ushort)Convert.ToByte(n.Attributes.GetNamedItem("dlc").Value);
cyclic = Convert.ToInt32(n.Attributes.GetNamedItem("cyclicrate").Value);
XmlNode sn = n.SelectSingleNode("bytes");
for (ushort i = 0; i < dlc; i++)
{
try
{
bytes[i] = Convert.ToInt32(sn.ChildNodes.Item(i).InnerText, 16);
}
catch(Exception e)
{
bytes[i] = 0x0;
Console.WriteLine(String.Format("Error Building can Message: {0}", e.ToString()));
}
}
CanMessage cm = new CanMessage(name, ecuname, id, dlc, bytes, cyclic);
CanList.Add(cm);
}
My list is being created with no issues. My question is after my list has been created I will sometimes need to do some bit manipulation on certain bytes of a certain Canmessage. How can I select a message from the list based on its name property and then edit certain bytes from that message? I know how to select a message from the list using lambda expression and linq. But I don't know how to then combine that select method with an edit and save method or if this is even the best way to go about doing this.
If i understand your problem statement correctly, you need to find a specific CanMessage in the List<CanMessage> and edit its properties to your liking. Since your CanMessage is an object, it's being accessed by reference and therefore your edits will get reflected everywhere you reference it from.
Consider the following:
{
var CanList = new List<CanMessage>(); // I am assuming this is what it is
BuildCanList(CanList);
var messagetoEdit = CanList.First(m => m.Name == "BODY9");
messagetoEdit.Bytes[1]= 0xff;
messagetoEdit.Bytes[2]= 0xff;
messagetoEdit.Bytes[3]= 0xff;
messagetoEdit.Bytes[4]= 0xff;
var newMessagetoEdit = CanList.First(m => m.Name == "BODY9"); // you will see that values have changed here
//in case you wanted to serialise the list back, heres a snippet on how you could do it, for more details see https://stackoverflow.com/questions/6161159/converting-xml-to-string-using-c-sharp
//this is just to prove a point that changing bytes has effect,
StringWriter sw = new StringWriter();
var serialiser = new XmlSerializer(typeof(List<CanMessage>));
serialiser.Serialize(sw, CanList);
sw.ToString();
}
I hope this clarifies
using c# I have read an XML file into an XML document and I use XPath to get a list of XMLNodes in an XMLNodeList codelist. I am wanting to create a separate List BlankCodes that has reference to any XmlNodes in codelist that meet a criteria.
so my current code that creates a list of XmlNodes of interest looks like the below.
XmlDocument xDoc = new XmlDocument();
xDoc.Load("C:\\Test.XML");
List<int> blankCodes = new List<int>();
XmlNodeList codeList = xDoc.SelectNodes("codelist\\Code");
foreach( XmlNode aNode in codeList)
{
if(aNode.Value == "")
{
blankCodes.Add( (int)aNode.Attributes("Index")
}
}
I will then iterate through the integers in the blankCodes list and find the corresponding node in the codeList again and modify another value in the node.
is it possible to essentially create a list of Pointers to the appropriate XmlNodes in codeList? then rather than having to find XmlNodes in codeList by xPath or looping through I can reference the XmlNode directly?
I am more than happy to try and provide clarification if you can guide on what needs clarifying.
Many Thanks bommelding.
------Demo code of working answer below -----------------
using System;
using System.Collections.Generic;
namespace TestReferences
{
/// <summary>
/// Basic Class to with index and value
/// </summary>
class aVal
{
public int Index { get; set; }
public int Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
//Create two lists of class to simulate XML node
List<aVal> originalList = new List<aVal>(); //Proper list as if read from XML
List<aVal> blankList = new List<aVal>(); //List of Blank Codes
//Loop to create 20 instances of class
for(int i = 1; i <= 20; i++)
{
//Create class
aVal temp = new aVal();
temp.Index = i; //Index
temp.Value = i * 2; //Easily Identifiable Value
originalList.Add(temp); //Add to original list
if( i == 4 || i==12 || i == 18) //Simulate Blank Codes
{
blankList.Add(originalList[originalList.IndexOf(temp)]); //Add the instance to blank list so we get a reference,
//I presume that I have to add the one in the list not the temporary instance used to populate the original list
}
}
//Write the original list to the console
//Expected output "Index 1 : Val 2"
Console.WriteLine("******* Original List ***************");
foreach( aVal te in originalList)
{
Console.WriteLine("Index {0} : Val {1}", te.Index, te.Value);
}
//Write the blank list to the console.
Console.WriteLine("******* Blank List ***************");
foreach (aVal te in blankList)
{
Console.WriteLine("Index {0} : Val {1}", te.Index, te.Value);
}
Console.WriteLine("*****************");
Console.WriteLine("Modifying Blanks");
Console.WriteLine("*****************");
//Set each instance.value to -99 referenced by the blanklist
foreach (aVal te in blankList)
{
te.Value = -99;
}
//Check the output, 4,12,18 should have value -99
Console.WriteLine("******* Original List after blanks modified ***************");
foreach (aVal te in originalList)
{
Console.WriteLine("Index {0} : Val {1}", te.Index, te.Value);
}
Console.ReadLine();
}
}
}
Yes, because XmlNodes are normal C# objects. You always work with (through) a reference.
Creating a copy of an instance is a lot more work, especially for the XmlDocument family.
List<XmlNode> blankCodes = new List<XmlNode>();
if(aNode.Value == "")
{
blankCodes.Add(aNode);
}
I'm using html agility pack and after I got array of nodes:
HtmlNode[] nodes = document.DocumentNode.SelectNodes("//tbody[#class='table']").ToArray();
now i want to run a for loop one each nodes[i]. I've tried this:
for (int i = 0; i < 1; i++)
{
if (t == null)
t = new Model.Track();
HtmlNode[] itemText = nodes[i].SelectNodes("//td[#class='artist']").ToArray();
for (int x = 0; x < itemText.Length; x++)
{ //doing something }
the problem is that the itemtext array isn't focusing on nodes[i] .
but brings out an array of all the ("//td[#class='artist']") in the html document.
help?
Using //td[#class='artist'] will fetch all columns with artist class from your document.DocumentNode.
Using .//td[#class='artist'] (Notice the dot at the begining) will fetch all columns with artist class from the current selected node, which in your case is nodes[i].
I want to get label of svg element. I wrote a console application and thougt svg file as xml. I am trying to get labels according to id of element. When i wrote code below, i got all ids but i cant get label with that row. How can i access the label? An example of element is;
<rect
style="fill:#cccccc"
id="21"
width="35.823246"
height="35.823246"
x="299.87155"
y="65.999405"
class="seatObj"
inkscape:label="A22" />
And codes are;
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.Load(#"seatChart-01.svg");
XmlNodeList elementList = doc.GetElementsByTagName("rect");
string[] labels = new string[elementList.Count];
for (int i = 0; i < elementList.Count; i++)
{
int id = int.Parse(elementList[i].Attributes["id"].Value);
labels[id] = elementList[i].Attributes["inkspace:label"].Value;
}
for (int i = 0; i < labels.Length; i++)
{
Console.WriteLine(labels[i]);
}
Console.ReadLine();
}
}
elementList[i].Attributes["inkspace:label"].Value
should be
elementList[i].Attributes["label", "http://www.inkscape.org/namespaces/inkscape"].Value
I'm assuming that the namespace is that of the inkscape drawing program here. To confirm that look for something on the root element that looks like this...
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
I have a large xml document that needs to be processed 100 records at a time
It is being done within a Windows Service written in c#.
The structure is as follows :
<docket xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="docket.xsd">
<order>
<Date>2008-10-13</Date>
<orderNumber>050758023</orderNumber>
<ParcelID/>
<CustomerName>sddsf</CustomerName>
<DeliveryName>dsfd</DeliveryName>
<Address1>sdf</Address1>
<Address2>sdfsdd</Address2>
<Address3>sdfdsfdf</Address3>
<Address4>dffddf</Address4>
<PostCode/>
</order>
<order>
<Date>2008-10-13</Date>
<orderNumber>050758023</orderNumber>
<ParcelID/>
<CustomerName>sddsf</CustomerName>
<DeliveryName>dsfd</DeliveryName>
<Address1>sdf</Address1>
<Address2>sdfsdd</Address2>
<Address3>sdfdsfdf</Address3>
<Address4>dffddf</Address4>
<PostCode/>
</order>
.....
.....
</docket>
There could be thousands of orders in a docket.
I need to chop this into 100 element chunks
However each of the 100 orders still need to be wrapped with the parent "docket" node and have the same namespace etc
is this possible?
Another naive solution; this time for .NET 2.0. It should give you an idea of how to go about what you want. Uses Xpath expressions instead of Linq to XML. Chunks a 100 order docket into 10 dockets in under a second on my devbox.
public List<XmlDocument> ChunkDocket(XmlDocument docket, int chunkSize)
{
List<XmlDocument> newDockets = new List<XmlDocument>();
//
int orderCount = docket.SelectNodes("//docket/order").Count;
int chunkStart = 0;
XmlDocument newDocket = null;
XmlElement root = null;
XmlNodeList chunk = null;
while (chunkStart < orderCount)
{
newDocket = new XmlDocument();
root = newDocket.CreateElement("docket");
newDocket.AppendChild(root);
chunk = docket.SelectNodes(String.Format("//docket/order[position() > {0} and position() <= {1}]", chunkStart, chunkStart + chunkSize));
chunkStart += chunkSize;
XmlNode targetNode = null;
foreach (XmlNode c in chunk)
{
targetNode = newDocket.ImportNode(c, true);
root.AppendChild(targetNode);
}
newDockets.Add(newDocket);
}
return newDockets;
}
Naive, iterative, but works [EDIT: in .NET 3.5 only]
public List<XDocument> ChunkDocket(XDocument docket, int chunkSize)
{
var newDockets = new List<XDocument>();
var d = new XDocument(docket);
var orders = d.Root.Elements("order");
XDocument newDocket = null;
do
{
newDocket = new XDocument(new XElement("docket"));
var chunk = orders.Take(chunkSize);
newDocket.Root.Add(chunk);
chunk.Remove();
newDockets.Add(newDocket);
} while (orders.Any());
return newDockets;
}
If the reason to process 100 orders at a time is for performance purposes, e.g. taking too much time and resource to open a big file, You can utilize XmlReader to process order element one at a time without degrading the performance.
XmlReader reader = XmlReader.Create(#"c:\foo\Doket.xml")
while( reader.Read())
{
if(reader.LocalName == "order")
{
// read each child element and its value from the reader.
// or you can deserialize the order element by using a XmlSerializer and Order class
}
}