I am trying to parse a XML-file, that could have different element names, except the root.
I would like to get first a list of all "Stockholm", "London".. etc (names would be unknown to the application). Then i would like to get all sub-elements of each "Stockholm". I am trying to build a treeview with this information, where the parts "Stockholm" and "London" would be the root of each node, and the child elements with id/name/tag should get listed under each previous element.
An example how the XML-file would look like is this
<?xml version="1.0" encoding="utf-8"?>
<Nodes>
<Stockholm>
<id>101</id>
<Name>Södertälje</Name>
<Tag>digital2</Tag>
</Stockholm>
<Stockholm>
<id>102</id>
<Name>Kungens Kurva</Name>
<Tag>digtal1</Tag>
</Stockholm>
<London>
<id>201</id>
<Name>Bloomsbury</Name>
<Tag>digital2</Tag>
</London>
<London>
<id>202</id>
<Name>Brixton</Name>
<Tag>digtal1</Tag>
</London>
<Tokyo>
<id>301</id>
<Name>Ogasawara Island</Name>
<Tag>digtal1</Tag>
</Tokyo>
<Tokyo>
<id>302</id>
<Name>Izu</Name>
<Tag>digital2</Tag>
</Tokyo>
<Washington>
<id>401</id>
<Name>Pleasant Plaint</Name>
<Tag>digital2</Tag>
</Washington>
<Washington>
<id>402</id>
<Name>Georgetown</Name>
<Tag>digtal1</Tag>
</Washington>
</Nodes>
How would i go about to do this in silverlight?
All example code i have found, assumes one knows all elements by name.
Related
I'm always getting turned around with XML. It isn't my bag. I have a .net app that i'm trying to build to capture the needed information. Here is a sample:
<?xml version="1.0"?>
<DTS:Executable xmlns:DTS="www.microsoft.com/SqlServer/Dts">
<DTS:Executables>
<DTS:Executable>
<DTS:Variables />
<DTS:ObjectData>
<pipeline
version="1">
<components>
<component
usesDispositions="true"
version="4">
<properties>
<property
dataType="System.Int32"
description="The number of seconds before a command times out. A value of 0 indicates an infinite time-out."
name="CommandTimeout">0</property>
<property
dataType="System.String"
description="Destination"
name="OpenRowset">DestinationFile</property>
</properties>
</component>
</components>
</pipeline>
</DTS:ObjectData>
</DTS:Executable>
</DTS:Executables>
</DTS:Executable>
This is just a snippet of a larger file with many DTS:ObjectData and properties per.
I'm attempting to pull the value "DestinationFile" from the property where the name = OpenRowset.
Sounds like what you're looking for is a simple xpath:
//property[#name='OpenRowset']/text()
Look into System.XML.XMLReader. It's been a while since I've used it so I can't give you example code off the top of my head but the general idea is that the Read function can be used to iterate through the xml nodes until you find one with NodeType == XmlNodeType.Element and Name == "property", then MoveToAttribute("name") to get the element's name attribute, and check if its value is "OpenRowSet". If so, there's functions to get back to the attribute's parent element and read its data.
Having issues getting node values. Not sure why the following code is failing to do so.
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type='text/xsl' href='STIG_unclass.xsl'?>
<Benchmark xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cpe="http://cpe.mitre.org/language/2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" id="Windows_7_STIG" xml:lang="en" xsi:schemaLocation="http://checklists.nist.gov/xccdf/1.1 http://nvd.nist.gov/schema/xccdf-1.1.4.xsd http://cpe.mitre.org/dictionary/2.0 http://cpe.mitre.org/files/cpe-dictionary_2.1.xsd" xmlns="http://checklists.nist.gov/xccdf/1.1">
<status date="2015-06-16">accepted</status>
<title>Windows 7 Security Technical Implementation Guide</title>
<description>
The Windows 7 Security Technical Implementation Guide (STIG) is published as a tool to improve the security of Department of Defense (DoD) information systems. The requirements were developed from DoD consensus, as well as the Windows 7 Security Guide and security templates published by Microsoft Corporation. Comments or proposed revisions to this document should be sent via e-mail to the following address: disa.stig_spt#mail.mil.
</description>
<notice id="terms-of-use" xml:lang="en">Developed_by_DISA_for_the_DoD</notice>
<reference href="http://iase.disa.mil">
<dc:publisher>DISA, Field Security Operations</dc:publisher>
<dc:source>STIG.DOD.MIL</dc:source>
</reference>
<plain-text id="release-info">Release: 20 Benchmark Date: 24 Jul 2015</plain-text>
</Benchmark>
Sample XML File.
and the following is my code.
String Title = LoadedXML.Element("Benchmark").Attribute("id").Value;
var XMLData = LoadedXML.Element("Benchmark").Elements("plain-text")
.Single(release => release.Attribute("id").Value == "release-info").Value;
is there a way I can get multiple Node values at the same time? Like getting the Title and Release Value at once instead of having a separate one for each?
Your code is failing because your XML contains Namespace and you can't access your nodes directly. If you want to confirm this simply query LoadedXML.Elements() and examine the values in debugger, you can clearly see the namespaces there:-
So, You need to declare the namespace and use it:-
XNamespace ns = "http://checklists.nist.gov/xccdf/1.1";
If you want both vales to be fetched at once you can project it to a anonymous type like this:-
var result = LoadedXML.Root.Elements(ns + "plain-text")
.Where(x => (string)x.Attribute("id") == "release-info")
.Select(x => new
{
Title = (string)x.Document.Root.Attribute("id"),
XMLData = x.Value
}).FirstOrDefault();
This query is giving me below output:-
Linq-to-xml is generally used to query a XML to filter it's nodes and then get the desired element/values per need. It's more like querying a table with SQL.
If all/most of the XML is required as a result, then the better approach would be to deseralize the XMl into a native (C# here) object and map it to the required model object. XML can always be thought of a serialized version of an object (although it can be manually as well), and can be deserialized back to the actual object.
.Net has native support for all these, see msdn links for XML Serialization and Deserialization for details. You can write a small method to deserialize your object like this.
using System.Xml.Linq;
using System.Xml.Serialization;
public class XMLHelper
{
public T DeserializeData<T>(string data)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
StringReader reader = new StringReader(data);
var deserializedObject = serializer.Deserialize(reader);
return deserializedObject == null ? default(T) : (T)deserializedObject;
}
}
To get the string you can do like File.ReadAllText(xmlFilePath) or whatever is easier for the situation.
This will give you deseialized object of the whole XML. If you want some other transformed object, you can either manually map that, or use AutoMapper
I am using the CreateElement() method of my XmlDocument like this:
XmlElement jobElement = dom.CreateElement("job");
But what I get in the outcome is a job element with an empty namespace.
<job xmlns="">xyz</job>
And the program that is supposed to read this Xml will no longer detect this job element.
I thought this question gives an answer, but it does not. How can I create a pure job element?
<job>xyz</job>
Update: This is the root element of my document:
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
"I thought this question gives an answer, but it does not."
Actually it does. Set <job> element to be in the default namespace (unprefixed namespace that, in this case, was declared at the root level) :
XmlElement jobElement = dom.CreateElement("job", "http://quartznet.sourceforge.net/JobSchedulingData");
jobElement.InnerText = "xyz";
This way, in the XML markup, <job> element simply inherits it's ancestor's default namespace (no local-empty default namespace will be created) :
<job>xyz</job>
By using include tag I am trying to put comments for my code in separate file "docs.xml".
But it does not work. I have been trying both C# and VB.NET projects.
Here is my comments file:
<?xml version="1.0" encoding="utf-8" ?>
<d>
<summary>Demo summary</summary>
</d>
I have a class ABC with one single property Demo. before this property I write:
/// <include file="docs.xml" path="d/*" />
or in VB.NET:
''' <include file="docs.xml" path="d/*" />
However summary for ABC.Demo never appears in InteliSense / Object browser / another project (if I reference my project).
I have a strong feeling I am missing something here.
P.S. I have tried following "path[#name=]" pattern of XML file, but it does not help.
Perhaps you already saw this in the documentation then, but if I understand correctly, you have to do the following:
In Visual Studio, you specify the XML doc comments option in the Build
pane of the Project Designer. When the C# compiler sees the
tag, it will search for documentation comments in xml_include_tag.doc
instead of the current source file.
I have classes Project,Resource and File.
where
A Project contains LIST of Resources.
Each Resource contains LIST of of Files of particular type.
This is mapped to an XML :
<Project>
<Resource id=1>
<File id="1" path="" type="A" />
<File id="2" path="" type="B" />
<File id="3" path="" type="B" />
<File id="4" path="" type="B" />
</Resource>
<Resource id=2>
<File id="1" path="" type="A" />
<File id="2" path="" type="B" />
<File id="3" path="" type="B" />
<File id="4" path="" type="B" />
</Resource>
</Project>
So basically every resource has to have at-most one file of type "A" and any number of files of type "B" . The file type is selected by user from a dialog where he selects the file and adds to the resource.
The problem is for every file of type "A", i need to create a new Resource and hence and new node in XML.(which my current code isn't able to do)
Initially i came with the following (generalised for brevity)
Project p =new Project("Untitled project"); //Will happen once per project
Resource res = p.CreateProjectResource("resource1");
//various params to create resource
p.AddResource(res);
//now lets add files to a resource
AddFileHelper(res,"C:\myfile1.bin","A",guid.toString());
AddFileHelper(res,"C:\myfile32.bin","B",guid.toString());
AddFileHelper(res,"C:\myfile56.bin","B",guid.toString());
//The next statement should create a new resource and add the file to
//the new created design
AddFileHelper(res,"C:\myfile4.bin","A",guid.toString()); //
//some helper class :
//Adds a file of type "type" to a resource "res" with file ID as "id"
private AddFileHelper(Resource res,string path,FileType type,string id)
{
// path is user defined file path from OpenFile dialog,
//type is selected from a Dropdown (of Enum values "A","B",...)
//id is GUID
res.AddFile(path,type,id);
//************ OR it could be also written as *******
//ResFile file =new ResFile(path,type,id);
//res.AddFile(file);
//Update XML file here..
}
The main problem is the User does not create the resources "explicitly" (except for the first resource) and creation of new Resource depends on the type of the file being added by the user.
Also due this design it is difficult to figure out the Resource given a File id.
Only way to track is using the file collection in each Resource class.
Any help??
Thanks All.
This is in reference to a question I asked before post
The problem as I understand it:
As of now, your AddFileHelper only adds files to your project resource labeled ''resource1'' which is a problem because every time a filetype “A” is passed your AddFileHelper, you to make a new resource for your project (''resource2'') and add it to that.
There is a very simple way to do this. Within AddFileHelper test the FileType of the added file and determine whether or not you need a new resource to be added to your project. If the type isn't “A” you'll call the code that you have now and add the file with:
res.AddFile(path, type, id);
If the type to add is “A” and you need a new resource, just redefine res and increment a counter variable of how many resources you have in your project:
Resource res = p.CreateProjectResource(resourceName);
resourceCounter++;
Where resourceName is the string:
string resourceName = ''resource'' + resourceCounter;
All this should be implemented as your AddFileHelper method.
Regarding the overall structure of your code, the AddFileHelper should be a project class method. Here's why:
The AddFile method, and the AddFileHelper method sound similar but do two very different things. The AddFile method belongs to the resource class because it acts on a well defined resource object. However, the AddFile method is not enough for your purposes because the resource file to append to is not immediately apparent to the client who has a file and wants to add it to your project. Bbefore the AddFile method can be called, the target resource needs to be determined. The job of the AddFileHelper method is to determine which resource will call the AddFile method. Therefore, the AddFileHelper method belongs to the project class and the AddFile method to the resource class.
The logical connection between the AddFileHelper method and the project class might be more apparent if you renamed the method to FileResourceAssignment or something like that.