Merging similar xml documents - c#

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.

Related

Querying CIM XML in C# with LINQ

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

Inserting XML nodes and inner nodes to an existing XML document in C#

Currently I have a working C# program that works as follows:
Accept .xls template with values (xls is manually created by user)
Save the values (matching fields) to the database
Convert and write .xls to XML. Please see below sample output:
Existing XML Structure
Now, what I want to do is:
Read the existing xml (the created xml)
Insert another set of nodes and subnodes (ReleaseLine and sub nodes). It must accept multiple ReleaseLine.
Save/create the new xml with appended nodes. Please see below output:
This is what I'm looking for:
My existing C# program is simple but the XML nodes and hierarchy is bloody deep. I just created the C# code using new XElement method and passing values for each nodes. Then I simply use xmlDocument.Save() method to write the xml.
[Existing XML Program][3]
To add nodes or append content in existing xml-data I´d use Linq to XML.
XElement xml = XElement.Load("file.xml");
xml.Add( new XElement("uberNode",
new XElement("childNode", content),
new XElement("anotherChildNode", content)));
xml.Save("file.xml");
Here are some other related solutions.
Add to specific node (with example):
Following exisiting XML-data:
`<Names>
<Name>
<prename>John</prename>
<lastname>Snow</lastname>
</Name>
<Name>
<prename>Harry</prename>
<lastname>Harry</lastname>
</Name>
</Names>`
Now I want to add an "age"-tag before the first "prename"-tag and a "family"-tag after the first "lastname"-tag.
XElement xml = XElement.Load("file.xml");
var childrens = xml.DescendantsAndSelf().ToArray();
var first_prename = childrens[2];
var first_lastname = childrens[3];
Console.WriteLine(childrens[0]); //prints out the whole content
first_prename.AddBeforeSelf(new XElement("age", 22));
first_lastname.AddAfterSelf(new XElement("family", new XElement("mother", "paula"), new XElement("father", "paul")));
xml.Save("file.xml");
Outcome:
`<Names>
<Name>
<age>22</age>
<prename>John</prename>
<lastname>Snow</lastname>
<family>
<mother>paula</mother>
<father>paul</father>
</family>
</Name>
<Name>
<prename>Harry</prename>
<lastname>Harry</lastname>
</Name>
</Names>`
I was facing the problem and Linq gave me the easiest way to accomplish that!
There are also other similar way e.g. here. But I tried a bit more and DescendantsAndSelf() made it easier for me to go through.
I found an answer to my question, here is the link http://www.xmlplease.com/add-xml-linq
Using XPathSelectElement method, I was able to find the right node and appended new block of XElement.

How to get a second or third XML node when using an anonymous type?

I'm using an anonymous type to grab some XML data. All was going well until I ran across a section of XML where there can be 2 or 3 similar nodes. Like in the XML sample below there are 3 separate "Phones". My code was working fine when there was only ONE element that was possible to grab after following the "element path" I led it to. How can i grab a specific one? Or all 3 for that matter? Handling XML is still new to me and there seems to be soo many ways of handling it Searching the web for my exact need here didn't prove successful. Thanks.
var nodes = from node in doc.Elements("ClaimsSvcRs").Elements("ClaimDownloadRs")
select new
{
Phone1 = (string)node.Elements("Communications").Elements("PhoneInfo").Elements("PhoneNumber").FirstOrDefault(),
Phone2 = (string)node.Elements("Communications").Elements("PhoneInfo").Elements("PhoneNumber").FirstOrDefault(),
};
The XML Code is
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
<ClaimsSvcRs>
<ClaimDownloadRs>
<Communications>
<PhoneInfo>
<PhoneTypeCd>Phone</PhoneTypeCd>
<CommunicationUseCd>Home</CommunicationUseCd>
<PhoneNumber>+1-715-5553944</PhoneNumber>
</PhoneInfo>
<PhoneInfo>
<PhoneTypeCd>Phone</PhoneTypeCd>
<CommunicationUseCd>Business</CommunicationUseCd>
<PhoneNumber>+1-715-5552519</PhoneNumber>
</PhoneInfo>
<PhoneInfo>
<PhoneTypeCd>Phone</PhoneTypeCd>
<CommunicationUseCd>Cell</CommunicationUseCd>
<PhoneNumber>+1-715-5551212</PhoneNumber>
</PhoneInfo>
</Communications>
</ClaimDownloadRs>
</ClaimsSvcRs>
</TEST>
I haven't used xpath in a while so i'll let someone else stand in there... but there's a way to select a particular PhoneInfo object based upon its subelements. So if you knew whether you wanted Home or Business or Cell or whatever, you'd be able to select that particular PhoneInfo object. Otherwise if you wanted simple Phone1,2,3 and nulls where ok, use the Skip linq function. Phone2 = query.Skip(1).FirstOrDefault()
lol no worries ;) xpath can be intermixed in here, was my thought, and might be more elegant if your CommunicationUseCd fields were deterministic. Then you could have Home = ... and Work = ..., etc, instead of Phone1 & Phone2
The same could be accomplished by slipping a where clause into each your query lines
If you're up for LINQ you can get all your elements in one go:
foreach(XElement phone in XDocument.Parse(xmlString).Descendants("PhoneInfo"))
{
Console.WriteLine(phone.Element("PhoneNumber").Value);
//etc
}
I find XDocument & LINQ a lot easier than XmlDocument & XPath, if you're okay with the alternative. There's more info on them here

Programming Query for adding a Xelement or removing a xelement in Xelement file in C# .Net

i have the following XML file...
<TrebuchetWorkspaceTemplate>
<InputDocument>
<BusinessDocument>
<BusinessRequirement />
<ProcessModel />
</BusinessDocument>
<FunctionalDocuments />
<UseCases />
<Agile />
</InputDocument>
<Miscellaneous />
</TrebuchetWorkspaceTemplate>
Now what code can I write if I want
1) to find any particular XElement say "< BusinessRequirement/>"
2) add another new xelement child to it
3) also find any Xelement in xml file for removing it..
will be helpful if u reply with specific answers....or any pre existing code....Thanks once again for previous answers...
Now what code can I write if I want 1) to find any particular XElement say <BusinessRequirement/>
Well,
var element = doc.Descendants("BusinessRequirement").First();
will find the first such element - but what do you want to do if there aren't any? Or if there are multiple?
2) add another new xelement child to it
Call Add afterwards:
element.Add(new XElement("foo"));
3) also find any Xelement in xml file for removing it.
Call Remove:
element.Remove();
You should have been able to find all of these out by using a good tutorial or book on LINQ to XML though - you shouldn't try to learn a new technology just by asking questions here; it's important to learn it systematically, with a good guide. LINQ to XML is a wonderful API, but you really need to get to know its idioms.
I suggest you start off with the MSDN LINQ to XML main page.

Select child nodes, but ignore non-elements with XPath?

Given the following XML document for example:
<?xml version="1.0"?>
<UrdaObject>
<Date>
<Year>2011</Year>
<Month>5</Month>
<Day>18</Day>
<Hours>8</Hours>
<Minutes>47</Minutes>
<Seconds>36</Seconds>
</Date>
<random_value>24</random_value>
</UrdaObject>
And the understanding the child::node() - Selects all child nodes of the current node how would I create an XPath (starting from the root) that would select all child nodes EXCEPT text, comments, and other things that are NOT elements. For example, when using this code to create a tree view in WPF:
// x is some XmlDocument, xmlTree is my WPF TreeView
XmlDataProvider provider = new XmlDataProvider();
provider.Document = x;
Binding binding = new Binding();
binding.Source = provider;
binding.XPath = "child::node()";
xmlTree.SetBinding(TreeView.ItemsSourceProperty, binding);
How would I go about creating my XPath statement so I build a treeview with nodes going all the way down and stopping before the raw text? For example it would generate a view of:
UrdaObject
Date
Year
...
Instead of...
UrdaObject
Date
Year
2011 (Don't want this!)
...
The sample XML files is just for me to explain my situation. The expression should be able to navigate any valid XML file and pull the elements, but not the individual text.
How did we fix this? I had switched all references of child::node() to child::*. However, I had NOT corrected one line in my XAML, which was pulling child::node(). Correcting this line made the application behave correctly... and made me feel silly.
child::node() finds all child nodes. child::* finds all element nodes.
it's as simple as *.
(that gets immediate children, however; if you want all descendant elements, it would be descendant::*)
child::* will exclude text nodes and leave only element nodes
child::text() will include only text nodes
child::node() will include both element and text nodes
http://www.w3.org/TR/xpath/#location-paths
Not sure if this is what you want but could it be done this way?
var doc =XDocument.Parse(#"
<UrdaObject>
<Date>
<Year>2011</Year>
<Month>5</Month>
<Day>18</Day>
<Hours>8</Hours>
<Minutes>47</Minutes>
<Seconds>36</Seconds>
</Date>
<random_value>24</random_value>
</UrdaObject>
");
var query = from s in doc.Descendants()
select s.Name;
foreach (var name in query)
{
Console.WriteLine(name);
}

Categories