I am looking for a way to query CIM XML files with LINQ, in order to speed up my analysis of data and verify the exports from a database containing the elements of electrical network.
Since I am a newbie in C#, I guessed that it will be easy to load the CIM XML in console application and based on tag values filter some elements. However, all tutorials that I found so far are straight-forward where elements are named like "Student", "Purchase", etc.
My elements are named like "cim:LoadBreakSwitch", where "cim" is actual address defined in root node.
This way, when I try to select all elements named like "cim:LoadBreakSwitch", I get an exception thrown on Run Time because the name of a element cannot contain colon.
Example of element that I want to select from the CIM XML file:
<cim:LoadBreak rdf:ID="101">
<cim:ConductingEquipment.phases>A</cim:ConductingEquipment.phases>
<cim:IdentifiedObject.description>PoleMounted</cim:IdentifiedObject.description>
</cim:LoadBreak>
When I print in the console the names of all elements with
IEnumerable<XElement> elements = xmlDoc.Elements();
foreach (var item in elements)
{
Console.WriteLine(item.Name);
}
I get something like
"{http://[address from the root node]}LoadBreak".
I don't know if there is possibility to do it, but I am just curious did anyone who is experienced developer had need to do something similar.
Your XML is missing a root element with namespace declarations.
Here is a conceptual example for you. It shows how to handle namespaces and query XML with them.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<rdf:RDF xmlns:cim='http://iec.ch/TC57/2008/CIM-schema-cim13#'
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<cim:LoadBreak rdf:ID='101'>
<cim:ConductingEquipment.phases>A</cim:ConductingEquipment.phases>
<cim:IdentifiedObject.description>PoleMounted</cim:IdentifiedObject.description>
</cim:LoadBreak>
</rdf:RDF>");
XNamespace cim = xdoc.Root.GetNamespaceOfPrefix("cim");
XNamespace rdf = xdoc.Root.GetNamespaceOfPrefix("rdf");
foreach (XElement xelem in xdoc.Descendants(cim + "LoadBreak").Elements())
{
Console.WriteLine("{0}: {1}"
, xelem.Name.LocalName
, xelem.Value);
}
}
Output
ConductingEquipment.phases: A
IdentifiedObject.description: PoleMounted
Related
The situation
I'm using XDocument to load XML files from a lot of different sources. They all contain pretty much the same type of information, but the XML structure is very different.
This is a simplified example of what I'm doing today for a single source:
string path = #"some_file.xml";
var doc = XDocument.Load(path);
var root = doc.Root;
foreach (var node in root)
{
User u = new User();
u.Name = node.Element("User").Element("Information").Attribute("Name").Value;
u.Description = node.Element("User").Element("Description").Value;
u.Edited = node.Element("User").Element("Description").Attribute("Edited").Value;
/* ...and so on. */
listOfUsers.add(u);
}
This works, but I would like to make it more dynamic. As it happens, I already have strings containing exactly where to look for my properties formated like this:
"Element:User->Element:Information->Attribute:Name"
"Element:User->Element:Description"
"Element:User->Element:Description->Attribute:Edited"
This for example, tells me where in the structure I would find Name, Description and Edited in this particular XML file.
My question
Is there any way I can use these string to tell XDocument where to look? In other words, is there any way I can dynamically convert the above strings to this?
u.Name = node.Element("User").Element("Information").Attribute("Name").Value;
u.Description = node.Element("User").Element("Description").Value;
u.Edited = node.Element("User").Element("Description").Attribute("Edited").Value;
What I have tried
I found something called Reflection, that seems to do something similar to what I'm trying to do, but I can't figure out how to apply it to my problem above.
Maybe there are other ways as well?
Remember that you also have the option of XPath
So all you need to do is translate your string path from this: (After navigating root)
`Element:User->Element:Information->Attribute:Name`
to: (skip your root)
`/*/User/Information/#Name`
Which will then allow you to do like this:
var name = document.XPathSelectElement("/Root/User/Information/#Name").Value;
If Namespaces pose a problem, you also have the option of agnostic paths, with local-name(), e.g.
/*[local-name()='Root']/*[local-name()='User']/*[local-name()='Information']/#Name
This is driving me a little crazy. I am pulling an XML string from a database column and successfully creating an XDocument using XDocument.Parse. I've used linq to xml before to query xml trees but for some reason on this everything I am doing is returning null. Is it something to do with the namespace?
Here is a sampling of the text visualizer for my XDocument object:
// removed XML for privacy reasons
An example of the query I am trying:
XElement algorithmId = (from algoId in reportLogXml.Descendants(ALGORITHM_ID)
select algoId).FirstOrDefault();
I am using a constant for the string value and I have quadruple checked that the spelling matches as well as trying several different elements that are clearly in the document but they all return null. What am I doing wrong here?
Yes, it probably has to do with the namespace but also the <AlgorithmId> element has no descendants.
You can fix the ns problem like this:
//untested
XNameSpace ns0 = "http://schemas.datacontract.org/2004/07/Adapters.Adapter";
var ns1 = reportLogXml.Root.GetDefaultNamespace();
// check: ns0 and ns1 should be equal here
... in reportLogXml.Descendants(ns1 + ALGORITHM_ID)
Note that this is a special + operator, follow the format exactly.
I have XElement object which is my XML tree read from XML file. Now I want to check all the nodes in this tree to get first attribute name and value. Is there any simple way to go through all of the nodes (from root till leaves)? My XML file has got very many different and strange nodes - that's why it's harder to solve this issue. I thought about writing some recursion, but hope it's another way to solve that easier.
Maybe take a look to Xpath. an XPath like this //*[#id=42] could do the job.
It means get all nodes which have an attribute "id" of value 42.
You can do just //* which gonna returns all nodes in a tree.
Xpath :
http://msdn.microsoft.com/en-gb/library/ms950786.aspx
Syntax :
http://msdn.microsoft.com/en-us/library/ms256471.aspx
You can get all children elements using XElement.Elements().
Here's some code using recursion to get all elements of each level:
void GetElements(XElement element){
var elements = element.Elements();
foreach(Element e in elements){
//some stuff here
if(e.Elements() != null)
GetElements(e);
}
}
I have a simple XML file that looks something like:
<Institutions>
<FI name = "NameOne">
<longname>some text</longname>
<APIKey>some text</APIKey>
<connectstring>some text</connectstring>
</FI>
<FI name = "NameTwo">
<longname>some text</longname>
<APIKey>some text</APIKey>
<connectstring>some text </connectstring>
</FI>
</Institutions>
Using LINQ to XML I can grab the entire file, find all values for "longname", "APIKey" and "connectstring" but I can not figure out how to find all the "name" values or how to grab only the three pieces of information underneath each FI name value. Just to be clear, I will have NO IDEA what the name= values are in advance.
I'm using:
XElement root = XElement.Load("c:\\directory\\Data_Config.xml");
and
IEnumerable<XElement> Fis =
from el in root.Elements("Institutions")
select el;
to load the file, as per the MSDN documentation. All of it's references seem to imply knowledge of what the name value is that I would be querying.
I've googled, tried different Attribute/Element queries, all with no luck. I'm pretty sure it's something simple but it's evading me.
How do I get this data?
Thanks,
Jason
var xml = XElement.Load (#"c:\directory\Data_Config.xml");
var query =
from e in xml.Descendants("FI")
select e.Attribute("name").Value;
I have several XDocuments that look like:
<Test>
<element
location=".\jnk.txt"
status="(modified)"/>
<element
location=".\jnk.xml"
status="(overload)"/>
</Test>
In C#, I create a new XDocument:
XDocument mergedXmlDocs = new XDocument(new XElement("ACResponse"));
And try to add the nodes from the other XDocuments:
for (ti = 0; (ti < 3); ++ti)
{
var query = from xElem in xDocs[(int)ti].Descendants("element")
select new XElement(xElem);
foreach (XElement xElem in query)
{
mergedXmlDocs.Add(xElem);
}
}
At runtime I get an error about how the Add would create a badly-formed document.
What am I doing wrong?
Thanks...
(I saw this question -- Merge XML documents -- but creating an XSLT transform seemed like extra trouble for what seems like a simple operation.)
You are very close. Trying changing the line
mergedXmlDocs.Add(xElem);
to
mergedXmlDocs.Root.Add(xElem);
The problem is that each XML document can only contain 1 root node. Your existing code is trying to add all of the nodes at the root level. You need to add them to the existing top level node instead.
I am not sure what programming language you are using, but for most programming languages there is extensive XML support classes. Most of them allow parsing and even adding of element. I would have 1 main file that I would keep around and then parse each new one adding the elements from the new one into the master.
EDIT: Sorry it looks like you are already doing exactly this.