I have a little problem with reading an XML file on C#.
This is my XML file:
<?xml version = "1.0" encoding="ISO8859-1" standalone="yes" ?>
<SITES>
<SITE>
<ERROR_COUNTER>0</ERROR_COUNTER>
<PROD>0</PROD>
<LOGINFO>
<URL>http://site1.com/login</URL>
<LOGIN>login</LOGIN>
<PASSWORD>pass</PASSWORD>
<DELAYMAX>20</DELAYMAX>
</LOGINFO>
<EMAIL>
<TO>dsds#dee.com, dsdsddd#dee.com,dsdds#dee.com</TO>
<SUBJECT></SUBJECT>
<BODY></BODY>
<PATH></PATH>
</EMAIL>
<TESTS>
<TEST>
<URL>http://site1.com/settings</URL>
<DELAYMAX>5</DELAYMAX>
</TEST>
<TEST>
<URL>http://site1.com/faq</URL>
<DELAYMAX>5</DELAYMAX>
</TEST>
<TEST>
<URL>http://site1.com/download-faq-pdf</URL>
<DELAYMAX>5</DELAYMAX>
</TEST>
</TESTS>
</SITE>
<SITE>
<ERROR_COUNTER>0</ERROR_COUNTER>
<PROD>0</PROD>
<LOGINFO>
<URL>http://site2.com/login</URL>
<LOGIN>login2</LOGIN>
<PASSWORD>pass2</PASSWORD>
<DELAYMAX>20</DELAYMAX>
</LOGINFO>
<EMAIL>
<TO>dsds#dee.com, dsdsddd#dee.com,dsdds#dee.com</TO>
<SUBJECT></SUBJECT>
<BODY></BODY>
<PATH></PATH>
</EMAIL>
<TESTS>
<TEST>
<URL>http://site2.com/settings</URL>
<DELAYMAX>5</DELAYMAX>
</TEST>
<TEST>
<URL>http://site2.com/faq</URL>
<DELAYMAX>5</DELAYMAX>
</TEST>
<TEST>
<URL>http://site2.com/download-faq-pdf</URL>
<DELAYMAX>5</DELAYMAX>
</TEST>
</TESTS>
</SITE>
</SITES>
So I would like to be able, in C#, to browse the list of my sites (I can have several dozen of them), for that, I did this:
XmlDocument xml = new XmlDocument();
xml.Load(#"D:\REM\config.xml");
foreach (XmlElement ndSites in xml.SelectNodes("SITES/SITE"))
{
Console.WriteLine(ndSites.GetElementsByTagName("ERROR_COUNTER"));
}
But once I have done this, I don't know how to access my children's values, let alone how to make a second loop on my TESTS, and then access each child TEST and retrieve my URLs.
My goal is being to retrieve for each website the connection information in LOGINFO, then the URLs in TESTS, TEST and for all the SITES present.
I've tried a lot of things, but I haven't found anything that matches what I need.
If someone could help me.
EDIT : This is my class for deserialize the code C#
class XMLParser
{
public XMLParser() { }
public List<Site> readXML(XmlDocument xml)
{
List<Test> tests = new List<Test>();
Test test;
List<Site> sites = new List<Site>();
Site site;
//List<url>;
foreach (XmlElement ndSites in xml.SelectNodes("SITES/SITE"))
{
int idSite = int.Parse(ndSites.Attributes[0].Value);
site = new Site();
int prod = int.Parse(ndSites["PROD"].InnerText);
int error = 0;
int max = 3;
foreach (XmlElement ndError in ndSites.SelectNodes("/ERROR"))
{
error = int.Parse(ndError["COUNTER"].InnerText);
max = int.Parse(ndError["MAX"].InnerText);
}
foreach (XmlElement ndLogin in ndSites.SelectNodes("LOGINFO"))
{
site = new Site(idSite, ndLogin["URL"].InnerText, ndLogin["LOGIN"].InnerText, ndLogin["PASSWORD"].InnerText, error, prod, max);
}
foreach (XmlElement ndTests in ndSites.SelectNodes("TESTS/TEST"))
{
test = new Test(ndTests["URL"].InnerText, int.Parse(ndTests["DELAYMAX"].InnerText));
tests.Add(test);
}
site.lstTest = tests;
sites.Add(site);
}
return sites;
}
But my i have another problem, when i use my Site object, o have six elements, all the URLs of my site 1 and all the url of my site 2.
For exemple with my XML file above, i would therefore have two Test objects of six elements
Simply you can work with reference value, your code if true, but I completed well.
when you want to work with an xml code, you have to know that structure as well.now lock this:
XmlDocument xml = new XmlDocument();
xml.Load(#"D:\REM\config.xml");
foreach (XmlElement ndSites in xml.SelectNodes("your node"))
{
Console.WriteLine(ndSites.GetElementsByTagName("your node"));
}
This method, GetElementsByTagName() get All xml section that you want and you can work with that like:
XmlDocument xml = new XmlDocument();
xml.Load(#"D:\REM\config.xml");
foreach (XmlElement ndSites in xml.SelectNodes("your node"))
{
var xmlNode = ndSites.GetElementsByTagName("your node");
if(xmlNode != null && xmlNode.contain("your node that you want"))
{
foreach(var item in xmlNode.GetElementByTagName(your node that you want))
{
console.write(item.value);
}
}
}
Fore more information, it is good that visit this
I'm sure you can do something like this:
var MiXMLD = new XmlDocument();
MiXMLD.Load(#"C:\path\MiXML.xml");
if (!(MiXMLD == null))
{
XmlElement mXMLERaiz = MiXMLD.DocumentElement;
foreach (XmlNode mXMLN in mXMLERaiz.ChildNodes)
{
// if type is <SITE>
if ((mXMLN.Name == "SITE"))
{
//In this point you can see the attributes
if ((mXMLN.Attributes.Count > 0))
{
foreach (XmlAttribute mAtr in mXMLN.Attributes)
{
if ((mAtr.Name == "Exalmple"))
{
var attr = mAtr.Value;
}
else if ((mAtr.Name == "Example"))
{
var attr = mAtr.Value;
}
}
}
// first node child of element of <parent>
// you already know the type <Type>
XmlElement mXMLchild = (XmlElement)mXMLN.FirstChild;
// aniway check the Type
if ((mXMLchild.Name == "Type"))
{
var mChild = mXMLchild.FirstChild.Value;
}
}
}
}
Of course you have to read every element.
I probably missunderstand what is your problem. Just store your xml data in business class like
public class Site
{
public int ErrorCounter { get; set; }
public LogInfo LogInfos { get; set; }
public Email Email { get; set; }
public List<Test> Tests { get; set; } = new List<Test>();
}
You unserialize your xml inside a list of your class, with the same way you already do. Then you can access all your data
List<Site> siteList = new List<Site>();
XmlDocument xml = new XmlDocument();
xml.Load(#"D:\REM\config.xml");
foreach (XmlElement ndSites in xml.SelectNodes("SITES/SITE"))
{
siteList.ErrorCounter = int.Parse(ndSites["ERROR_COUNTER"].innerText); // You should handle the potential parse error expression
/** do it for all you need */
foreach (XmlElement ndTests in ndSites.SelectNodes("TESTS/TEST"))
{
Test currentTest = new Test();
/** Fill it **/
siteList.Tests.add(currentTest);
}
}
Something about that, i don't have ide for test.
Hope it was your question
You can use these Wrappers. Load the XML and put it into the the Wrapper.
public class TestsXmlWrapper
{
private XmlDocument _xml;
public TestsXmlWrapper(XmlDocument xml)
{
_xml = xml;
}
public IEnumerable<Site> Sites
{
get
{
foreach (XmlElement site in _xml.SelectNodes("SITES/SITE"))
{
yield return new Site(site);
}
}
}
}
public class Site
{
private XmlElement _site;
public Site(XmlElement site)
{
_site = site;
}
public String ErrorCount => _site.SelectSingleNode("ERROR_COUNTER")?.InnerText;
public String LoginUrl => _site.SelectSingleNode("LOGINFO/URL")?.InnerText;
public String Username => _site.SelectSingleNode("LOGINFO/LOGIN")?.InnerText;
public String Password => _site.SelectSingleNode("LOGINFO/PASSWORD")?.InnerText;
public IEnumerable<Test> Test
{
get
{
foreach (XmlElement test in _site.SelectNodes("TESTS/TEST"))
{
yield return new Test(test);
}
}
}
}
public class Test
{
private XmlElement _test;
public Test(XmlElement test)
{
_test = test;
}
public String Url => _test.SelectSingleNode("URL")?.InnerText;
}
...the same for email
Related
When I serialize the value : If there is no value present in for data then it's coming like below format.
<Note>
<Type>Acknowledged by PPS</Type>
<Data />
</Note>
But what I want xml data in below format:
<Note>
<Type>Acknowledged by PPS</Type>
<Data></Data>
</Note>
Code For this i have written :
[Serializable]
public class Notes
{
[XmlElement("Type")]
public string typeName { get; set; }
[XmlElement("Data")]
public string dataValue { get; set; }
}
I am not able to figure out what to do for achieve data in below format if data has n't assign any value.
<Note>
<Type>Acknowledged by PPS</Type>
<Data></Data>
</Note>
You can do this by creating your own XmlTextWriter to pass into the serialization process.
public class MyXmlTextWriter : XmlTextWriter
{
public MyXmlTextWriter(Stream stream) : base(stream, Encoding.UTF8)
{
}
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}
You can test the result using:
class Program
{
static void Main(string[] args)
{
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(Notes));
var writer = new MyXmlTextWriter(stream);
serializer.Serialize(writer, new Notes() { typeName = "Acknowledged by PPS", dataValue="" });
var result = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(result);
}
Console.ReadKey();
}
If you saved your string somewhere (e.g a file) you can use this simple Regex.Replace:
var replaced = Regex.Replace(File.ReadAllText(name), #"<([^<>/]+)\/>", (m) => $"<{m.Groups[1].Value.Trim()}></{m.Groups[1].Value.Trim()}>");
File.WriteAllText(name, replaced);
IMO it's not possibe to generate your desired XML using Serialization. But, you can use LINQ to XML to generate the desired schema like this -
XDocument xDocument = new XDocument();
XElement rootNode = new XElement(typeof(Notes).Name);
foreach (var property in typeof(Notes).GetProperties())
{
if (property.GetValue(a, null) == null)
{
property.SetValue(a, string.Empty, null);
}
XElement childNode = new XElement(property.Name, property.GetValue(a, null));
rootNode.Add(childNode);
}
xDocument.Add(rootNode);
XmlWriterSettings xws = new XmlWriterSettings() { Indent=true };
using (XmlWriter writer = XmlWriter.Create("D:\\Sample.xml", xws))
{
xDocument.Save(writer);
}
Main catch is in case your value is null, you should set it to empty string. It will force the closing tag to be generated. In case value is null closing tag is not created.
Kludge time - see Generate System.Xml.XmlDocument.OuterXml() output thats valid in HTML
Basically after XML doc has been generated go through each node, adding an empty text node if no children
// Call with
addSpaceToEmptyNodes(xmlDoc.FirstChild);
private void addSpaceToEmptyNodes(XmlNode node)
{
if (node.HasChildNodes)
{
foreach (XmlNode child in node.ChildNodes)
addSpaceToEmptyNodes(child);
}
else
node.AppendChild(node.OwnerDocument.CreateTextNode(""))
}
(Yes I know you shouldn't have to do this - but if your sending the XML to some other system that you can't easily fix then have to be pragmatic about things)
You can add a dummy field to prevent the self-closing element.
[XmlText]
public string datavalue= " ";
Or if you want the code for your class then Your class should be like this.
public class Notes
{
[XmlElement("Type")]
public string typeName { get; set; }
[XmlElement("Data")]
private string _dataValue;
public string dataValue {
get {
if(string.IsNullOrEmpty(_dataValue))
return " ";
else
return _dataValue;
}
set {
_dataValue = value;
}
}
}
In principal, armen.shimoon's answer worked for me. But if you want your XML output pretty printed without having to use XmlWriterSettings and an additional Stream object (as stated in the comments), you can simply set the Formatting in the constructor of your XmlTextWriter class.
public MyXmlTextWriter(string filename) : base(filename, Encoding.UTF8)
{
this.Formatting = Formatting.Indented;
}
(Would have posted this as a comment but am not allowed yet ;-))
Effectively the same as Ryan's solution which uses the standard XmlWriter (i.e. there's no need for a derived XmlTextWriter class), but written using linq to xml (XDocument)..
private static void AssignEmptyElements(this XNode node)
{
if (node is XElement e)
{
e.Nodes().ToList().ForEach(AssignEmptyElements);
if (e.IsEmpty)
e.Value = string.Empty;
}
}
usage..
AssignEmptyElements(document.FirstNode);
I searched and tried some attributes but none of them worked for my below scenario:
public class ObjSer
{
[XmlElement("Name")]
public string Name
{
get; set;
}
}
//Code to serialize
var obj = new ObjSer();
obj.Name = "<tag1>Value</tag1>";
var stringwriter = new System.IO.StringWriter();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(stringwriter, obj);
Output would be as follows:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
But I need output as:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
Scenario 2: In some cases I need to set:
obj.Name = "Value";
Is there any attribute or method I can override to make it possible?
You can't with default serializer. XmlSerializer does encoding of all values during serialization.
If you want your member to hold xml value, it must be XmlElement. Here is how you can accomplish it
public class ObjSer
{
[XmlElement("Name")]
public XmlElement Name
{
get; set;
}
}
var obj = new ObjSer();
// <-- load xml
var doc = new XmlDocument();
doc.LoadXml("<tag1>Value</tag1>");
obj.Name = doc.DocumentElement;
// --> assign the element
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
Output:
<?xml version="1.0" encoding="IBM437"?>
<ObjSer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>
<tag1>Value</tag1>
</Name>
</ObjSer>
UPDATE:
In case if you want to use XElement instead of XmlElement, here is sample on how to do it
public class ObjSer
{
[XmlElement("Name")]
public XElement Name
{
get; set;
}
}
static void Main(string[] args)
{
//Code to serialize
var obj = new ObjSer();
obj.Name = XDocument.Parse("<tag1>Value</tag1>").Document.FirstNode as XElement;
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
}
No, you can't. It is the natural and usual behaviour of the xml serializer. The serializer doesn't have to handle within the XML strings. So, it escapes the xml as expected.
You should decode the escaped string again while deserializing it.
If you want to add dynamically elements in the XML I suggest you to use Linq to XML and you can create tag1 or another kind of elements easily.
I would suggest serializing to an XDocument then converting that to a string and manually unescaping the desired part and writing it to a file. I would say this would give you the least headache it shouldn't be more than a couple lines of code. If you need it I can provide some code example if needed.
I found one more way of changing the type
public class NameNode
{
public string tag1
{
get; set;
}
[XmlText]
public string Value
{
get; set;
}
}
public class ObjSer
{
[XmlElement("Name")]
public NameNode Name
{
get; set;
}
}
To set value of Name:
var obj = new ObjSer();
var valueToSet = "<tag1>Value</tag1>";
//or var valueToSet = "Value";
//With XML tag:
if (valueToSet.Contains("</"))
{
var doc = new XmlDocument();
doc.LoadXml(valueToSet);
obj.Name.tag1 = doc.InnerText;
}
else //Without XML Tags
{
obj.Name.Value = senderRecipient.Name;
}
This solution will work in both cases, but has limitation. It will work only for predefined tags (ex. tag1)
I need to deserialize an XML document that looks like this:
<Root>
<Items>
<Item>
<ItemHeader Attr1="A" Attr2="B" Attr3="C" />
<ItemDetails Attr4="D" Attr5="E" />
</Item>
...
</Items>
</Root>
Into a class that looks like this:
[Serializable, XmlRoot("Item")]
Public class MyItem
{
[XmlAttribute("Attr1")]
public string Attr1 { get; set; }
[XmlAttribute("Attr5")]
public string Attr5 { get; set; }
}
And I am using the following code to perform the deserialization:
XDocument doc;
XElement rootElem = doc.Element("Root");
foreach (XElement xe in rootElem.Descendants("Item"))
{
MyItem item = new MyItem();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyItem));
XmlReader xRdr = xe.CreateReader();
item = (MyItem)xmlSerializer.Deserialize(xRdr);
}
However, none of the elements are copied into the object instance.
Is this doable? Do I need to deserialize each sub Element?
Thanks
I'm not sure there's a way to do that using the default XML serializer via attributes, without doing the whole class structure to match your XML - so ItemHeader and ItemDetails would need their own class.
You can implement the IXmlSerializable interface though, so you can completely customize - if you must keep the structure of MyItem as it is.
static void Main(string[] args)
{
XmlSerializer myItemSerializer = new XmlSerializer(typeof(MyItem));
var xmlDoc = XDocument.Parse(#"<Item>
<ItemHeader Attr1=""A"" Attr2=""B"" Attr3=""C"" />
<ItemDetails Attr4=""D"" Attr5=""E"" />
</Item>");
using (var reader = xmlDoc.CreateReader())
{
MyItem myItem = (MyItem)myItemSerializer.Deserialize(reader);
}
Console.Read();
}
[Serializable, XmlRoot("Item")]
public class MyItem : IXmlSerializable
{
[XmlAttribute("Attr1")]
public string Attr1 { get; set; }
[XmlAttribute("Attr5")]
public string Attr5 { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
reader.ReadStartElement("Item");
do
{
switch (reader.Name)
{
case "ItemHeader":
Attr1 = reader.GetAttribute("Attr1");
reader.Read();
break;
case "ItemDetails":
Attr5 = reader.GetAttribute("Attr5");
reader.Read();
break;
default:
throw new XmlException(String.Format("{0} was not expected", reader.Name));
}
} while (reader.Name != "Item");
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("ItemHeader");
writer.WriteAttributeString("Attr1", Attr1);
writer.WriteEndElement();
writer.WriteStartElement("ItemDetails");
writer.WriteAttributeString("Attr5", Attr5);
writer.WriteEndElement();
}
}
The issue is the xml data you are receiving does not match the serialization attributes of MyItem, schemas differ. There are multiple ways around this but I guess the quickest and dirtiest solution would be to extract the part that interests you:
XDocument doc = XDocument.Load (#"Your.xml");
XElement rootElem = doc.Element("Root");
XElement itemsElem = rootElem.Element("Items");
foreach (XElement xe in itemsElem.Elements("Item"))
{
MyItem item = new MyItem()
{
Attr1 = xe.Element("ItemHeader").Attribute("Attr1").Value,
Attr5 = xe.Element("ItemDetails").Attribute("Attr5").Value
};
}
I have a class that various different XML schemes are created from. I create the various dynamic XDocuments via one (Very long) statement using conditional operators for optional elements and attributes.
I now need to convert the XDocuments back to the class but as they are coming from different schemes many elements and sub elements may be optional. The only way I know of doing this is to use a lot of if statements.
This approach doesn't seem very LINQ and uses a great deal more code than when I create the XDocument so I wondered if there is a better way to do this?
An example would be to get
<?xml version="1.0"?>
<root xmlns="somenamespace">
<object attribute1="This is Optional" attribute2="This is required">
<element1>Required</element1>
<element1>Optional</element1>
<List1>
Optional List Of Elements
</List1>
<List2>
Required List Of Elements
</List2>
</object>
</root>
Into
public class Object()
{
public string Attribute1;
public string Attribute2;
public string Element1;
public string Element2;
public List<ListItem1> List1;
public List<ListItem2> List2;
}
In a more LINQ friendly way than this:
public bool ParseXDocument(string xml)
{
XNamespace xn = "somenamespace";
XDocument document = XDocument.Parse(xml);
XElement elementRoot = description.Element(xn + "root");
if (elementRoot != null)
{
//Get Object Element
XElement elementObject = elementRoot.Element(xn + "object");
if(elementObject != null)
{
if(elementObject.Attribute(xn + "attribute1") != null)
{
Attribute1 = elementObject.Attribute(xn + "attribute1");
}
if(elementObject.Attribute(xn + "attribute2") != null)
{
Attribute2 = elementObject.Attribute(xn + "attribute2");
}
else
{
//This is a required Attribute so return false
return false;
}
//If, If/Elses get deeper and deeper for the next elements and lists etc....
}
else
{
//Object is a required element so return false
return false;
}
}
else
{
//Root is a required element so return false
return false;
}
return true;
}
Update: Just to clarify the ParseXDocument method is inside the "Object" class. Every time an xml document is received the Object class instance has some or all of it's values updated.
Have a look at XElement and XAttribute Type Conversions.
The document root is always non-null.
Validate the object afterwards, so you don't have to implement validation twice (once when parsing and once before saving).
Code:
private static readonly XNamespace xn = "somenamespace";
public bool ParseXDocument(string xml)
{
XDocument document = XDocument.Parse(xml);
var obj = document.Root.Element(xn + "object");
if (obj == null)
return false;
Attribute1 = (string)obj.Attribute("attribute1");
Attribute2 = (string)obj.Attribute("attribute2");
Element1 = (string)obj.Element(xn + "element1");
Element2 = (string)obj.Elements(xn + "element1").ElementAtOrDefault(1);
// ...
return Validate();
}
Below I have a couple of extension methods you can use that may help reach your desired readability. The code in it's current form does not support returning true/false depending on if all the required elements were found. I recommend adopting dtb's advice in that regard, and just parse out the details, and then perform validation on that.
There are downsides, such as repeated iteration over the XML sturcture, but with a bit of imagination I'm sure you could overcome that if it becomes an issue.
Here is what MyObject looks like:
public class MyObject
{
public string Attribute1;
public string Attribute2;
public string Element1;
public string Element2;
public List List1;
public List List2;
public void ParseXDocument(string xml)
{
XNamespace xn = "somenamespace";
XDocument document = XDocument.Parse(xml);
XElement elementRoot = document.Root;
elementRoot.MatchElement(xn.GetName("object"), xObject => {
xObject.MatchAttribute("attribute1", (x,a) => this.Attribute1 = (string)a);
xObject.MatchAttribute("attribute2", (x,a) => this.Attribute2 = (string)a);
xObject.MatchElement(xn.GetName("element1"), x => this.Element1 = (string)x);
xObject.MatchElement(xn.GetName("element2"), x => this.Element2 = (string)x);
});
}
}
Here are the extension methods that support it:
public static class XElementExtensions {
public static XElement MatchAttribute(this XElement x, XName name, Action<XElement, XAttribute> action) {
foreach (var a in x.Attributes(name)) {
action(x, a);
}
return x;
}
public static XElement MatchElement(this XElement x, XName name, Action<XElement> action) {
foreach (var child in x.Elements(name)) {
action(child);
}
return x;
}
}
And finally, here is some sample driver code I used in LinqPad to test it out:
void Main()
{
var xml = #"<?xml version=""1.0""?>
<root xmlns=""somenamespace"">
<object attribute1=""This is Optional"" attribute2=""This is required"">
<element1>Required</element1>
<element2>Optional</element2>
<List1>
Optional List Of Elements
</List1>
<List2>
Required List Of Elements
</List2>
</object>
</root>
";
var o = new MyObject();
o.ParseXDocument(xml);
o.Dump();
}
I have a small part of a xml file named "fragment.xml" which is shown below where each "SteamStuff" has their own "AttributeValue" value .So here if i click a copy button, i need to create small xml file contains only the datas of SteamStuff named "pirate".For this i have created windows form containing two buttons named "Copy" and "Paste".If i click "Copy" button it should find "fragment.xml" where it is located in my PC and need to find the managed object named "pirate" (<Text>pirate</Text>) once it finds there will be a guid <ID>b6792ed8-680b-4117-a695-9f7ef2c7752b</ID> assosiated with it ,so whereever this ID appears it should select and datas and create small xml file named "test.xml" on the same location of "fragment.xml" . So test.xml contains only datas of "pirate"
What code i can implement here in button1_Click() event
<?xml version="1.0" standalone="yes"?>
<LeafDataSchema xmlns="http://tempuri.org/LeafDataSchema.xsd">
<SteamStuff>
<ID>b6792ed8-680b-4117-a695-9f7ef2c7752b</ID>
<Label>pirate</Label>
<Owner>00000000-0000-0000-0000-000000000000</Owner>
<Image>00000000-0000-0000-0000-000000000000</Image>
<ClassID>00000000-0000-0000-0000-000000000008</ClassID>
<DefaultApp>00000000-0000-0000-0000-000000000000</DefaultApp>
<Name>pirate</Name>
<Volatile>true</Volatile>
</SteamStuff>
<AttributeValue>
<ID>2977f4e0-84ab-4ad2-8c4d-6bcb49727889</ID>
<ObjectID>b6792ed8-680b-4117-a695-9f7ef2c7752b</ObjectID>
<Text>True</Text>
<AttributeName>Monitor</AttributeName>
<ClassID>00000000-0000-0000-0000-000000000008</ClassID>
</AttributeValue>
-------------
Using these xml extensions: http://searisen.com/xmllib/extensions.wiki
Creating classes, because it makes it easier to see how it works:
public class XFile
{
XElement self;
public XFile(string file)
{
self = XElement.Load(file);
}
public XNamespace Namespace
{
get { return _Namespace ?? (_Namespace = self.GetDefaultNamespace()); }
}
XNamespace _Namespace;
public AttributeValue[] AttributeValues
{
get
{
return _AttributeValues ??
(_AttributeValues = self.GetEnumerable("AttributeValue", x => new AttributeValue(this, x)).ToArray());
}
}
AttributeValue[] _AttributeValues;
public SteamStuff[] SteamStuffs
{
get
{
return _SteamStuffs ??
(_SteamStuffs = self.GetEnumerable("SteamStuff", x => new SteamStuff(this, x)).ToArray());
}
}
SteamStuff[] _SteamStuffs;
}
public class SteamStuff
{
XElement self;
XFile parent;
public SteamStuff(XFile parent, XElement self)
{
this.parent = parent;
this.self = self;
}
public XElement Element { get { return self; } }
public string ID
{
get { return self.Get("ID", string.Empty); }
}
public string Label
{
get { return self.Get("Label", string.Empty); }
}
}
public class AttributeValue
{
XElement self;
XFile parent;
public AttributeValue(XFile parent, XElement self)
{
this.parent = parent;
this.self = self;
}
public XElement Element { get { return self; } }
public string ObjectID
{
get { return self.Get("ObjectID", string.Empty); }
}
}
XFile xfile = new XFile("test.xml");
SteamStuff[] pirates = xfile.SteamStuffs
.Where(steam => steam.Label == "pirate")
.ToArray();
AttributeValue[] associated = xfile.AttributeValues
.Where(av => pirates.Any(pirate => pirate.ID == av.ObjectID))
.ToArray();
// write to new file
XElement fragments = new XElement(xfile.Namespace + "fragments");
foreach(SteamStuff steamStuff in pirates)
fragments.Add(steamStuff.Element);
foreach(AttributeValue value in associated)
fragments.Add(value.Element);
fragments.Save("fragment_file.xml");
Edit: Extensions file adjusts for the 'default' namespace.