How do I merge two XDocuments removing duplicates - c#

I have two XML files (*.resx files) that I am trying to merge in to one removing duplicates, but am unable to do so. I've tried the following without any success:
var resource1 = XDocument.Load("C:\\Resources.resx");
var resource2 = XDocument.Load("C:\\Resources2.resx");
// This results in a file with all the nodes from the second file included inside
// the root element of the first file to form a properly formatted, concatenated file.
resource1.Descendants().FirstOrDefault().Add(resource2.Descendants().FirstOrDefault().Nodes());
var nodeContent = new List<string>();
foreach (XElement node in resource1.Root.Elements())
{
if (nodeContent.Contains(node.ToString()))
resource1.Remove();
else
nodeContent.Add(node.ToString());
}
resource1.Save("C:\\FinalResources.resx");
On the remove statement I get an InvalidOperationException - "The parent is missing.":
Am I doing something wrong?

You need to define an EqualityComparer<XElement> that will enable you to use the standard LINQ operators.
So, as a simple example I created this:
public class ElementComparer : EqualityComparer<XElement>
{
public override int GetHashCode(XElement xe)
{
return xe.Name.GetHashCode() ^ xe.Value.GetHashCode();
}
public override bool Equals(XElement xe1, XElement xe2)
{
var #return = xe1.Name.Equals(xe2.Name);
if (#return)
{
#return = xe1.Value.Equals(xe2.Value);
}
return #return;
}
}
So I can then start with these two XML documents:
<xs>
<x>D</x>
<x>A</x>
<x>B</x>
</xs>
<xs>
<x>E</x>
<x>B</x>
<x>C</x>
</xs>
And do this:
var xml1 = XDocument.Parse(#"<xs><x>D</x><x>A</x><x>B</x></xs>");
var xml2 = XDocument.Parse(#"<xs><x>E</x><x>B</x><x>C</x></xs>");
xml1.Root.Add(
xml2.Root.Elements("x")
.Except(xml1.Root.Elements("x"), new ElementComparer()));
Then xml1 will look like this:
<xs>
<x>D</x>
<x>A</x>
<x>B</x>
<x>E</x>
<x>C</x>
</xs>

Well, the most straight forward way is:
var resource1 = XDocument.Load("C:\\Resources.resx");
var resource2 = XDocument.Load("C:\\Resources2.resx");
foreach (XElement node in resource2.Root.Elements())
{
if (resource1.Root.Contains(node)) continue;
resource1.Add(node);
}
resource1.Save("C:\\FinalResources.resx");
public static class XElementExtensions
{
public static bool Contains(this XElement root, XElement e)
{
//or w/e equality logic you need
return root.Elements().Any(x => x.ToString().Equals(e.ToString()));
}
}
This will only merge first level entries tho. If you need deep merge, then you will have to set up a simple recursion (using the same loop for child elements).

resource1.Remove(); is called twice and what it does is remove the root element. So the second time there is no longer a root element to remove from and thus throwing the exception.

Related

Checking multiple XML files

I am re-wording this from my original post:
I have two XML files, and they are related to a given year each. For example, 18/19 and 17/18. They conform to the same structure and below is small sample from one of these files. What I want is, in C#, to compare all records in these files where the Given Name, the Family Name, the NI Number and the Date of birth are the same, BUT the Learner Ref Number is different. I need to be able to compare, then push only these records into a data table so I can then push them into a spreadsheet (the spreadsheet bit I can do). I currently have the below as a starting block, but am still very much stuck.
Firstly, I have my Import button press for which:
private void Btn_Import_Click(object sender, RoutedEventArgs e)
{
ILRChecks.ILRReport.CrossYear();}
Then this goes to look at the Class of which eventually pushes the file to my location:
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ILRValidation;
using InfExcelExtension;
namespace ILRChecks
{
internal static partial class ILRReport
{
internal static void CrossYear()
{
DataSet ds_CrossYearChecks =
ILRValidation.Validation.CrossYearChecks(Global.fileNames);
string output = Path.Combine(Global.foldername, "ULIN_Issues" +
".xlsx");
ds_CrossYearChecks.ToWorkBook(output);
}
}
}
And this is the bit I'm stuck on, which is the production of finding the differences:
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ILRValidation
{
public static partial class Validation
{
public static DataSet CrossYearChecks(DataSet ds_CrossYearChecks)
{
return CrossYearChecks(ds_CrossYearChecks);
}
public static DataSet CrossYearChecks(string[] xmlPath)
{
DataSet ds_xmlCrossYear = new DataSet();
return CrossYearChecks(ds_xmlCrossYear);
}
}
}
XML:
<Learner>
<LearnRefNumber></LearnRefNumber>
<ULN></ULN>
<FamilyName></FamilyName>
<GivenNames></GivenNames>
<DateOfBirth></DateOfBirth>
<Ethnicity></Ethnicity>
<Sex></Sex>
<LLDDHealthProb></LLDDHealthProb>
<NINumber></NINumber>
<PriorAttain></PriorAttain>
<MathGrade></MathGrade>
<EngGrade></EngGrade>
<PostcodePrior></PostcodePrior>
<Postcode></Postcode>
<AddLine1></AddLine1>
<AddLine3></AddLine3>
<Email></Email>
Well, you can traverse both XML files recursively and write down all the encountered changes. Something like should be helpful:
static string AppendPrefix(string oldPrefix, string addition) =>
oldPrefix == "" ? addition : $"{oldPrefix}.{addition}";
static void CompareElements(string prefix, XElement d1, XElement d2)
{
// 1. compare names
var newPrefix = AppendPrefix(prefix, d1.Name.ToString());
if (d1.Name != d2.Name)
{
Console.WriteLine(
$"Name mismatch: {newPrefix} != {AppendPrefix(prefix, d2.Name.ToString())}");
return;
}
// 2. compare attributes
var attrs = d1.Attributes().OrderBy(a => a.Name);
var unpairedAttributes = new HashSet<XAttribute>(d2.Attributes());
foreach (var attr in attrs)
{
var otherAttr = d2.Attributes(attr.Name).SingleOrDefault();
if (otherAttr == null)
{
Console.WriteLine($"No new attr: {newPrefix}/{attr.Name}");
continue;
}
unpairedAttributes.Remove(otherAttr);
if (attr.Value != otherAttr.Value)
Console.WriteLine(
$"Attr value mismatch: {newPrefix}/{attr.Name}: {attr.Value} != {otherAttr.Value}");
}
foreach (var attr in unpairedAttributes)
Console.WriteLine($"No old attr: {newPrefix}/{attr.Name}");
// 3. compare subelements
var leftNodes = d1.Nodes().ToList();
var rightNodes = d2.Nodes().ToList();
var smallerCount = Math.Min(leftNodes.Count, rightNodes.Count);
for (int i = 0; i < smallerCount; i++)
CompareNodes(newPrefix, i, leftNodes[i], rightNodes[i]);
if (leftNodes.Count > smallerCount)
Console.WriteLine($"Extra {leftNodes.Count - smallerCount} nodes at old file");
if (rightNodes.Count > smallerCount)
Console.WriteLine($"Extra {rightNodes.Count - smallerCount} nodes at new file");
}
static void CompareNodes(string prefix, int index, XNode n1, XNode n2)
{
if (n1.NodeType != n2.NodeType)
{
Console.WriteLine($"Node type mismatch: {prefix}/[{index}]");
return;
}
switch (n1.NodeType)
{
case XmlNodeType.Element:
CompareElements(prefix, (XElement)n1, (XElement)n2);
break;
case XmlNodeType.Text:
CompareText(prefix, index, (XText)n1, (XText)n2);
break;
}
}
static void CompareText(string prefix, int index, XText t1, XText t2)
{
if (t1.Value != t2.Value)
Console.WriteLine($"Text mismatch at {prefix}[{index}]");
}
Usage:
XDocument d1 = <get document #1 from somewhere>,
d2 = <get document #2 from somewhere>;
CompareNodes("", 0, d1.Root, d2.Root);
Obviously, instead of writing to console you should write to the appropriate spreadsheet.
Note that I'm ignoring the attribute reorder but not subnode reorder (which seems to be right).
Seems to me you're having trouble extracting the values you want from the xml, correct?
As the others have mentioned in the comments, without knowing the layout of your xml its impossible to give a specific example for your case. If you edit your question to include an example of your xml, we can help more.
Here are some general examples of how to extract values from xml:
private static bool CheckXmlDocument(string xmlPathCheck)
{
// if you have multiple files from which you need to extract values, pass in an array or List<string> and loop over it, fetching the values
// XmlDocument will allow you to edit the document as well as read it
// there's another option to use XPathDocument and XPathNavigator but it's read-only
var doc = new XmlDocument();
// this can throw various exceptions so might want to add some handling
doc.Load(xmlPathCheck);
// getting the elements, you have some options depending on the layout of the document
// if the nodes you want are identified by 'id' use this:
var nameElement = doc.GetElementById("name");
// if the nodes you want are identified by 'tag', use this:
var nameNodeList = doc.GetElementsByTagName("name");
// if you know the xpath to the specific node you want, use this:
var selectNameNode = doc.SelectSingleNode("the/xpath/to/the/node");
// if there are several nodes that have the same xpaths, use this:
var selectNameList = doc.SelectNodes("the/xpath/that/may/match/many/nodes");
// getting the value depends on the if you have an XmlNode, XmlElement or XmlNodeList
// if you have a single XmlElement or XmlNode you can get the value one of these ways depending on the layout of your document:
var name = nameElement.InnerText;
name = nameElement.InnerXml;
// if you have an XmlNodeList, you'll have to iterate through the nodes to find the one you want, like this:
foreach (var node in nameNodeList)
{
// here use some condition that will determine if its the element/node you want or not (depends on your xml layout)
if (node is XmlNode n)
{
name = n.InnerText;
}
}
// do that for all the values you want to compare, then compare them
return CheckValues(/*the values to compare*/);
}
XmlDocument
XmlNode
XmlElement

Cannot find item name C# XML

I'm having a problem with my XML document.
I want my program to find all values of the items in my XML file, but only if the handlingType is of a certain character bunch.
Code (C#) :
string path = "//files//handling.meta";
var doc = XDocument.Load(path);
var items = doc.Descendants("HandlingData").Elements("Item");
var query = from i in items
select new
{
HandlingName = (string)i.Element("handlingName"),
HandlingType = (string)i.Element("HandlingType"),
Mass = (decimal?)i.Element("fMass")
};
foreach (var HandlingType in items)
{
if (HandlingType.ToString() == "HANDLING_TYPE_FLYING")
{
MessageBox.Show(HandlingType.ToString());
}
}
The above code demonstraights a short version of what I want to happen, but fails to find this handlingType (does not show the messageBox)
Here's the XML :
<CHandlingDataMgr>
<HandlingData>
<Item type="CHandlingData">
<handlingName>Plane</handlingName>
<fMass value="380000.000000"/>
<handlingType>HANDLING_TYPE_FLYING</handlingType>
</Item>
<Item type="CHandlingData">
<handlingName>Car1</handlingName>
<fMass value="150000.000000"/>
<handlingType>HANDLING_TYPE_DRIVING</handlingType>
</Item>
</HandlingData>
</CHandlingDataMgr>
I would like the output to show the handlingName if it contains a certain HandlingType
For e.g.
if (handlingType == "HANDLING_TYPE_FLYING")
{
messageBox.Show(this.HandlingName);
}
My problem in short : Program does not find item's handling type, it does find the tag but when asked to display, returns empty/shows as nothing.
Edit: Also in the XML handling_type_flying contains extra elements such as thrust that cannot be found in each item (such as car), I would like the program to also find these elements. (this is a second problem I'm facing, maybe should ask 2nd ques?)
Several things that need fixing.
you are not using your query in your foreach loop. foreach (var item in query)
Your element has an upercase "H" but should be lowercase "handlingType". HandlingType = (string)i.Element("handlingType"),
You are not pulling the Attribute value of your fMass element.Mass = i.Element("fMass").Attribute("value").Value
Once you adjust your Query in your foreach loop you then need to adjust the loop to account for looping over your newly made object.
NOTE that I removed (decimal) from Mass = i.Element("fMass").Attribute("value").Value
here is the code with all the fixes.
class Program
{
static void Main()
{
const string path = "//files//handling.meta";
var doc = XDocument.Load(path);
var items = doc.Descendants("HandlingData").Elements("Item");
var query = from i in items
select new
{
HandlingName = (string)i.Element("handlingName"),
HandlingType = (string)i.Element("handlingType"),
Mass = i.Element("fMass").Attribute("value").Value
};
foreach (var item in query)
{
if (item.HandlingType == "HANDLING_TYPE_FLYING")
{
//Remove messagebox if consoleapp
MessageBox.Show(item.HandlingType);
MessageBox.Show(item.HandlingName);
Console.WriteLine(item.HandlingType);
Console.WriteLine(item.HandlingName);
}
}
}
}
I would recommend looking into serializing your xml to an object.
If you look at http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement(v=vs.110).aspx the ToString() method doesn't return the name of the tag, but the indented XML.
You should instead be using the Value property. Also you should use .equals("...") instead of ==
if (handlingType.Value.equals("HANDLING_TYPE_FLYING")
{
messageBox.Show(this.handlingname);
}

Limit query to ignore elements descendants

I'm trying to query the following XML to build some objects which copy the folder hierarchy outlined in the XML.
<ShareList>
<Title>Documantis</Title>
<Url>/sites/dev/Documantis/Forms/AllItems.aspx</Url>
<Guid>fed8f456-efa9-4fe5-8b97-46734a3040b6</Guid>
<HasUniqueScopes>False</HasUniqueScopes>
<RootFolder>/sites/dev</RootFolder>
<Children>
<ShareListItem>
<Title>First</Title>
<Url>Documantis/First</Url>
<HasUniqueRole>False</HasUniqueRole>
<IsSubFolder>False</IsSubFolder>
<PermissionMask>FullMask</PermissionMask>
<Children>
<ShareListItem>
<Title>Second</Title>
<Url>Documantis/First/Second</Url>
<HasUniqueRole>False</HasUniqueRole>
<IsSubFolder>False</IsSubFolder>
<ParentGuid>22b2a7e9-a42e-497f-aad3-8caa85f6ac6d</ParentGuid>
</ShareListItem>
</Children>
</ShareListItem>
<ShareListItem>
<Title>Folda</Title>
<Url>Documantis/Folda</Url>
<HasUniqueRole>False</HasUniqueRole>
<IsSubFolder>False</IsSubFolder>
<PermissionMask>FullMask</PermissionMask>
</ShareListItem>
</Children>
</ShareList>
I'm having trouble finding a way to return one level of the <ShareListItem> elements at a time, with my current code it returns all the ShareListItems in one List which doesn't represent the hierarchy accurately.
XmlDocument doc = new XmlDocument();
doc.LoadXml(sharepointXml);
XElement root;
using (XmlReader xr = new XmlNodeReader(doc)) { root = XElement.Load(xr); }
var result = from child in root.DescendantsAndSelf("ShareList") //.Elements("ShareList") // Descendants("ShareList")
select child;
foreach (XElement xml in result)
{
// Build ListItem from results
ShareList list = new ShareList()
{
Title = xml.Element("Title").Value,
Url = xml.Element("Url").Value,
Guid = xml.Element("Guid").Value,
HasUniqueScopes = Convert.ToBoolean(xml.Element("HasUniqueScopes").Value),
RootFolder = xml.Element("RootFolder").Value,
};
if (xml.Element("Children") != null)
{
var subResult = from child in xml.Element("Children").Descendants("ShareListItem")
select child;
foreach (XElement subXml in subResult)
{
// results here are flat and don't show depth of nodes
}
//list.Children =
}
I could recursively infer the hierarchy's structure from the URL Element, however I already have it represented in XML so I would rather learn how to return this through a query.
Edit:
Here's what I ended up using
public List<ShareList> HandleLists(XElement levelRoot)
{
List<ShareList> lists = new List<ShareList>();
var results = from list in levelRoot.DescendantsAndSelf("ShareList")
select list;
foreach (var list in results)
{
var children = list.Element("Children");
if (children == null)
return null;
ShareList shareList = new ShareList()
{
Title = list.Element("Title").Value,
Url = list.Element("Url").Value,
Guid = list.Element("Guid").Value,
HasUniqueScopes = Convert.ToBoolean(list.Element("HasUniqueScopes").Value),
RootFolder = list.Element("RootFolder").Value,
// Recursively find ListItem folders
Children = HandleSubfolders(list)
};
lists.Add(shareList);
}
return lists;
}
public List<ShareListItem> HandleSubfolders(XElement levelRoot)
{
List<ShareListItem> subfolders = new List<ShareListItem>();
// All nodes deeper than current
var children = levelRoot.Element("Children");
if (children == null)
return null;
// Subfolders
var items = children.Elements("ShareListItem");
foreach (var item in items)
{
ShareListItem listItem = new ShareListItem()
{
Title = item.Element("Title").Value,
Url = item.Element("Url").Value,
HasUniqueRole = Convert.ToBoolean(item.Element("HasUniqueRole").Value),
IsSubfolder = Convert.ToBoolean(item.Element("IsSubFolder").Value),
PermissionMask = item.Element("PermissionMask").Value,
PermissionMaskName = item.Element("PermissionMaskName").Value,
// Recursively find ListItem subfolders
Children = HandleSubfolders(item)
};
// Add subfolder to Children collection
subfolders.Add(listItem);
}
return subfolders;
}
You would want to use recursion here.
Create a method that handles one level of the hierarchy and calls itself with the next level.
public void HandleLevel(XElement levelRoot)
{
PerformAction(levelRoot);
var children = levelRoot.Element("Children");
if(children == null)
return;
var items = children.Elements("ShareListItem");
foreach(var item in item)
{
// Handle child's children:
HandleLevel(item);
}
}
PerformAction is the code that actually does, whatever you want to do for each document.
The way the code is currently structured, this action is also executed for the root document /sites/dev/Documantis/Forms/AllItems.aspx.
If you don't want this simply move PerformAction into the foreach loop and pass item instead of levelRoot.
BTW: Your initialization of the root element is very strange.
You can simply use this:
var root = XDocument.Parse(sharepointXml).Root;
The initial call to HandleLevel would simply look like this:
HandleLevel(root);
A good way of producing the results your after is by using XPath (here's a good primer if you need it).
Once you've got your XML into an XmlDocument you can return different bits of it by using an XPathNavigator, like this:
var xmlNavigator = xmlDocument.CreateNavigator();
var outerQuery = xmlNavigator.Select("ShareList/Children/ShareListItem");
while (outerQuery.MoveNext()) {
Console.WriteLine(outerQuery.Current.SelectSingleNode("Title").Value);
var innerQuery = outerQuery.Current.Select("Children/ShareListItem");
while (innerQuery.MoveNext()) {
Console.WriteLine(" - " + innerQuery.Current.SelectSingleNode("Title").Value);
}
}
In the code above, we query the XML for all ShareListItem nodes within Children nodes of the root ShareList node, and store the resultant XPathNodeIterator in the variable outerQuery. We then iterate over all the nodes found, and run an operation as well as another XPath query on each to retrieve child nodes to process. The code above produces the following output:
First
- Second
Folda
Which I think it what you're after. Obviously, you can use recursion if necessary if your XML can be nested more deeply than this.
One way of doing this is by creating Classes to represent the hierarchy like so:
public class ShareList {
...
public List<ShareList> Children { get; set; }
}
In your code, refactor the traversing part into a method that accepts a Sharelist node and traverse it, calling itself for each Children node:
private Sharelist RecurseHierarchy(XElement sharelistNode, ShareList parent)
{
// your traversing code goes here
// get your data and create a new Sharelist object
// if it has a children node, traverse it and call this same method on the child
// Sharelist nodes
parent.Title = sharelistNode.Element("Title").Value;
var children = sharelistNode.Element("Children");
if (children != null)
{
var items = children.Elements("ShareListItem");
foreach(var listItem in items)
{
ShareList childShareList = new ShareList();
parent.Children.Add(childShareList);
RecurseHierarchy(listItem, childShareList);
}
}
// Just in case we want to chain some method
return parent;
}
To call it initially, you will have to pass in the root node and a new ShareList object.

Arrange TreeView by getting file paths?

I have this code:
public void AddNode(string Node)
{
try
{
treeView.Nodes.Add(Node);
treeView.Refresh();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Very simple as you see, this method gets file path. like C:\Windows\notepad.exe
Now i want the TreeView to show it like FileSystem..
-C:\
+Windows
And if i click the '+' it gets like this:
-C:\
-Windows
notepad.exe
Here is what i get now from sending theses pathes to the method above:
How can i do that it will arrange the nodes?
If I were you, I would split the input string onto substrings, using the string.Split method and then search for the right node to insert the relevant part of a node. I mean, that before adding a node, you should check whether node C:\ and its child node (Windows) exist.
Here is my code:
...
AddString(#"C:\Windows\Notepad.exe");
AddString(#"C:\Windows\TestFolder\test.exe");
AddString(#"C:\Program Files");
AddString(#"C:\Program Files\Microsoft");
AddString(#"C:\test.exe");
...
private void AddString(string name) {
string[] names = name.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
TreeNode node = null;
for(int i = 0; i < names.Length; i++) {
TreeNodeCollection nodes = node == null? treeView1.Nodes: node.Nodes;
node = FindNode(nodes, names[i]);
if(node == null)
node = nodes.Add(names[i]);
}
}
private TreeNode FindNode(TreeNodeCollection nodes, string p) {
for(int i = 0; i < nodes.Count; i++)
if(nodes[i].Text.ToLower(CultureInfo.CurrentCulture) == p.ToLower(CultureInfo.CurrentCulture))
return nodes[i];
return null;
}
If you are in windows forms (and I guess so), you can implement the IComparer class and use the TreeView.TreeViewNodeSorter property:
public class NodeSorter : IComparer
{
// Compare the length of the strings, or the strings
// themselves, if they are the same length.
public int Compare(object x, object y)
{
TreeNode tx = x as TreeNode;
TreeNode ty = y as TreeNode;
// Compare the length of the strings, returning the difference.
if (tx.Text.Length != ty.Text.Length)
return tx.Text.Length - ty.Text.Length;
// If they are the same length, call Compare.
return string.Compare(tx.Text, ty.Text);
}
}
Is the issue that the parents and children aren't being differentiated?
Each one of the nodes in the tree also has a Nodes property, which represents the collection of its children. Your AddNode routine needs to be changed so you can specify the parent node to whom you want to add a child node. Like:
TreeNode parent = //some node
parent.Nodes.Add(newChildNode);
If you want it to just populate the paths and figure out the parent-child relationships itself, you're going to have to write some code to parse the paths, and identify the parent node based on the path segments.
Try taking a look at this Filesystem TreeView. It should do exactly what you are looking for.

How can I transform XML into a List<string> or String[]?

How can I transform the following XML into a List<string> or String[]:
<Ids>
<id>1</id>
<id>2</id>
</Ids>
It sounds like you're more after just parsing rather than full XML serialization/deserialization. If you can use LINQ to XML, this is pretty easy:
using System;
using System.Linq;
using System.Xml.Linq;
public class Test
{
static void Main()
{
string xml = "<Ids><id>1</id><id>2</id></Ids>";
XDocument doc = XDocument.Parse(xml);
var list = doc.Root.Elements("id")
.Select(element => element.Value)
.ToList();
foreach (string value in list)
{
Console.WriteLine(value);
}
}
}
In fact the call to Elements could omit the argument as there are only id elements, but I thought I'd demonstrate how to specify which elements you want.
Likewise I'd normally not bother calling ToList unless I really needed a List<string> - without it, the result is IEnumerable<string> which is fine if you're just iterating over it once. To create an array instead, use ToArray.
Here is a way using XmlDocument :
// A string containing the XML data
string xml = "<Ids><id>1</id><id>2</id></Ids>";
// The list you want to fill
ArrayList list = new ArrayList();
XmlDocument doc = new XmlDocument();
// Loading from a XML string (use Load() for file)
doc.LoadXml(xml);
// Selecting node using XPath syntax
XmlNodeList idNodes = doc.SelectNodes("Ids/id");
// Filling the list
foreach (XmlNode node in idNodes)
list.Add(node.InnerText);
With any type of collection.
For example :
<Ids>
<id>C:\videotest\file0.txt</id>
<id>C:\videotest\file1.txt</id>
</Ids>
class FileFormat
{
public FileFormat(string path)
{
this.path = path;
}
public string FullPath
{
get { return Path.GetFullPath(path); }
}
public string FileName
{
get { return Path.GetFileName(path); }
}
private string path;
}
List<FileFormat> Files =
selectedNode
.Descendants("Ids")
.Elements("Id")
.Select(x => new FileFormat(x.Value))
.Where(s => s.FileName!=string.Empty)
.ToList();
the code to convert a string array to xml
items is a string array
items.Aggregate("", (current, x) => current + ("<item>" + x + "</item>"))
This sample will work with the .NET framework 3.5:
System.Xml.Linq.XElement element = System.Xml.Linq.XElement.Parse("<Ids> <id>1</id> <id>2</id></Ids>");
System.Collections.Generic.List<string> ids = new System.Collections.Generic.List<string>();
foreach (System.Xml.Linq.XElement childElement in element.Descendants("id"))
{
ids.Add(childElement.Value);
}
This sample will work with the .NET framework 4.0:
into a List
List<string> Ids= new List<string>();
Ids= selectedNode.Descendants("Ids").Elements("Id").Select(> x=>x.Value).Where(s =>s!= string.Empty).ToList();
into a string []
string[] Ids= thisNode
.Descendants("Ids") // Ids element
.Elements("Id") // Id elements
.Select(x=>x.Value) // XElement to string
.Where(s =>s!= string.Empty) // must be not empty
.ToArray(); // string to string []
You can use Properties class.
Properties prop = new Properties();
prop.loadFromXML(stream);
Set set = prop.keySet();
You can than access Strings from set for each key. Key is element name of xml.
Here's a way to get typed array from xml by using DataSets. (in this example array of doubles)
DataSet dataSet = new DataSet()
DoubleArray doubles = new DoubleArray(dataSet,0);
dataSet.ReadXml("my.xml");
double a = doubles[0];
public class DoubleArray
{
DataSet dataSet;
int tableIndex;
public DoubleArray(DataSet dataSet,int tableIndex)
{
this.dataSet=dataSet;
this.tableIndex=tableIndex;
}
public double this[int index]
{
get
{
object ret = dataSet.Tables[tableIndex].Rows[index];
if(ret is double?)
return (ret as double?).Value;
else
return double.Parse(ret as string);
}
set
{
object out = dataSet.Tables[tableIndex].Rows[index];
if(out is double?)
dataSet.Tables[tableIndex].Rows[index] = (double?)value;
else
dataSet.Tables[tableIndex].Rows[index] = value.ToString();
}
}
}
Of course parsing doubles and converting them back to strings all the time might be considered as blasphemy by some programmers... Even for me it was hard not to think about such waste of resources... but I guess sometimes it's better to just turn another away.. don't stress it :)

Categories

Resources