Merging issue in two xml documents using XDocument - c#

The existing XML file(test.xml) content is as under
<manifest xmlns:imsmd="http://ltsc.ieee.org/xsd/LOM">
<resources>
<resource>
<file href="default.xml">
<metadata>
<imsmd:lom />
</metadata>
</file>
</resource>
</resources>
</manifest>
What I need is to add new node so that the final output resembles:
<manifest xmlns:imsmd="http://ltsc.ieee.org/xsd/LOM">
<resources>
<resource>
<file href="default.xml">
<metadata>
<imsmd:lom />
</metadata>
</file>
<file href="testimg.png">
<metadata>
<imsmd:lom />
</metadata>
</file>
</resource>
</resources>
</manifest>
My try so far (though not entirely correct is as under)
XNamespace ns = #"http://ltsc.ieee.org/xsd/LOM";
XNamespace lomns = ":lom";
var doc = XDocument.Load(#"D:\test.xml");
var result = new XDocument(
new XElement(ns + "manifest",
new XElement("file", new XAttribute("href", "testimg.png"),
new XElement("metadata", new XElement(lomns + "imsmd"))
)
)
);
//merge
doc.Root.Add(result.Root.Elements());
whose output is
<manifest xmlns:imsmd="http://ltsc.ieee.org/xsd/LOM">
<resources>
<resource>
<file href="default.xml">
<metadata>
<imsmd:lom />
</metadata>
</file>
</resource>
</resources>
<file href="testimg.png">
<metadata>
<imsmd xmlns=":lom" />
</metadata>
</file>
</manifest>

Code is adding new element to the root, but as per your required output you mentioned a new element(file) should be resource.
Locate the element resource in your xml and then add the element.
doc.Descendants("resource")
.ElementAt(0)
.Add(new XElement("file", new XAttribute("href", "testimg.png"),
new XElement("metadata", new XElement(doc.Root.GetNamespaceOfPrefix("imsmd")+ "lom"))));
Output:
<manifest xmlns:imsmd="http://ltsc.ieee.org/xsd/LOM">
<resources>
<resource>
<file href="default.xml">
<metadata>
<imsmd:lom />
</metadata>
</file>
<file href="testimg.png">
<metadata>
<imsmd:lom />
</metadata>
</file>
</resource>
</resources>
</manifest>
Check this Demo

Related

Adding XML element to existing XML element in xml file C#

Please i'm trying to insert data into xml, this is the current format of my xml file after inserting data
<?xml version="1.0" encoding="utf-8"?>
<ApplicationData>
<Minutes>
</Minutes>
<Minute MinuteId="6" Title="Project6" Date="6" Time="Project6" Location="6" MinuteDocumentFile="Project6" />
<Minute MinuteId="6" Title="Project6" Date="6" Time="Project6" Location="6" MinuteDocumentFile="Project6" />
</ApplicationData>
using this code below
XmlTextReader _xmlTextReader = new XmlTextReader(config.XMLPath);
XmlDocument _xmlDocument = new XmlDocument();
_xmlDocument.Load(_xmlTextReader);
//Note: Close the reader object to release the xml file. Else while saving you will get an error that it is
//being used by another process.
_xmlTextReader.Close();
XmlElement _minutesElement = _xmlDocument.CreateElement("Minute");
_minutesElement.SetAttribute("MinuteId", "6");
_minutesElement.SetAttribute("Title", "Project6");
_minutesElement.SetAttribute("Date", "6");
_minutesElement.SetAttribute("Time", "Project6");
_minutesElement.SetAttribute("Location", "6");
_minutesElement.SetAttribute("MinuteDocumentFile", "Project6");
_xmlDocument.DocumentElement.AppendChild(_minutesElement);
_xmlDocument.Save(config.XMLPath);
That above code works fine, but my challenge now is, i'm trying to achieve the current format of xml shown below
<?xml version="1.0" encoding="utf-8"?>
<ApplicationData>
<Minutes>
<Minute MinuteId="6" Title="Project6" Date="6" Time="Project6" Location="6" MinuteDocumentFile="Project6" />
<Minute MinuteId="6" Title="Project6" Date="6" Time="Project6" Location="6" MinuteDocumentFile="Project6" />
</Minutes>
</ApplicationData>
I want to store the created "Minute" XmlElement inside the "Minutes" Element, not outside it.
Thanks..
The line
_xmlDocument.DocumentElement.AppendChild(_minutesElement);
is just appending your new _minutesElement to the end of the tree. You need to tell it which element you want it to belong to.
Changing it instead to be:
_xmlDocument.DocumentElement["Minutes"].AppendChild(_minutesElement);
Gives:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationData>
<Minutes>
<Minute MinuteId="6" Title="Project6" Date="6" Time="Project6" Location="6" MinuteDocumentFile="Project6" />
</Minutes>
</ApplicationData>
var doc = XElement.Load(config.XMLPath);
var target = doc.Descendants("Minutes").First();
target.Add(new XElement("Minute", new XAttribute("MinuteId", 6), new ...));
doc.Save(config.XMLPath);

Delete multiple XML nodes in C# using Linq

I'm trying to remove multiple nodes that a particular element(path) contains a value but I'm receiving a System.NullReferenceException any help where I'm going wrong would I'be much appreciated.
My xml looks like this:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ApplicationData Version="12.5.1" RootPath="FireFox-FILES">
<RegistrySystem>
<DIR Operation="+" Path="C:\Temp\Microsoft\MediaPlayer\ShimInclusionList" />
<DIR Operation="+" Path="C:\Temp\MediaPlayer\ShimInclusionList\MM.EXE" />
<DIR Operation="+" Path="C:\Temp\MediaPlayer\ShimInclusionList\plugin-container.exe" />
<DIR Operation="+" Path="C:\Temp\Microsoft\MediaPlayer">
<ENTRY Name="" Value="43.0.4" Type="1" />
<ENTRY Name="CurrentVersion" Value="43.0.4 (x86 en-GB)" Type="1" />
</DIR>
<DIR Operation="+" Path="C:\Program Files\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe" />
<DIR Operation="+" Path="C:\Program Files\Microsoft\MediaPlayer\ShimInclusionList2\plugin.exe" />
<DIR Operation="+" Path="C:\Program Files\Microsoft\MediaPlayer\ShimInclusionList2\container.exe" />
<DIR Operation="+" Path="C:\Program Files\Microsoft\MediaPlayer\ShimInclusionList4">
<ENTRY Name="" Value="43.0.4" Type="1" />
<ENTRY Name="CurrentVersion" Value="43.0.4 (x86 en-GB)" Type="1" />
</DIR>
</RegistrySystem>
</ApplicationData>
My code looks like this:
XDocument xdoc = XDocument.Load(XmlFile);
foreach (var node in xdoc.Descendants("DIR").Where(status => status.Attribute("Path").Value.Contains(#"C:\Temp\")))
{
node.Remove();
}
xdoc.Save(XmlFile);
I'm not sure where I'm going wrong.
I'm not sure why you're getting the exception, but I strongly suspect it's because you're modifying the document while you're querying it.
If you change your code to use a ToList() call to get the list of nodes to remove, that doesn't throw:
foreach (var node in xdoc.Descendants("DIR")
.Where(status => status.Attribute("Path").Value.Contains(#"C:\Temp\"))
.ToList())
{
node.Remove();
}
However, that's not the best way. The best approach is to use the Remove(this IEnumerable<XElement>) extension method:
xdoc.Descendants("DIR")
.Where(status => status.Attribute("Path").Value.Contains(#"C:\Temp\"))
.Remove();
No need for a foreach loop at all. Now to make it robust in the face of DIR elements without a Path attribute, you can cast to string instead:
xdoc.Descendants("DIR")
.Where(status => ((string) status.Attribute("Path") ?? "").Contains(#"C:\Temp\"))
.Remove();

Ignore child order while comparing the xml nodes

I have two xml document such as:
aDoc.xml:
<update id="1293">
<abc>.....</abc>
<xyz>....</xyz>
<components>
<component name="C1">
<files source="s1" target="t1">
<file>**file2**</file>
<file>**file1**</file>
</files>
</component>
</components>
</update>
bDoc.xml:
<update id="1293">
<abc>.....</abc>
<xyz>....</xyz>
<components>
<component name="C1">
<files source="s1" target="t1">
<file>**file1**</file>
<file>**file2**</file>
</files>
</component>
</components>
</update>
what i have tried so far is:
var DiffInA = aDoc.Descendants("component").Cast<XNode>().Except(bDoc.Descendants("component").Cast<XNode>(), new XNodeEqualityComparer());
the above line returns the output as below:
<component name="C1">
<files source="s1" target="t1">
<file>file2</file>
<file>file1</file>
</files>
(the node which is present in aDoc but not part of bDoc)
But if you look at both the xml files the component C1 is same only the order in which the tag appears is different.
Is there any way to achieve this using linq to xml.(i dont want to use XmlDiff or other Api's.)

XDocument.Descendants() returns NullReferenceException : Object reference not set to an instance of an object

I have some difficulties to retrieve some datas in my XML file.
This is what my XML looks like:
<?xml version="1.0" encoding="windows-1252"?>
<hexML version="0.9">
<head>
<title><![CDATA[Title ]]></title>
<description/>
<ftm date="2014-09-24T16:34:37 CET"/>
</head>
<body>
<press_releases>
<press_release id="1796257" language="fr" type="5">
<published date="2014-06-19T11:55:09 CET"/>
<categories>
<category id="75" label="French" keywords="language"/>
</categories>
<headline><![CDATA[Test Release for Website 3]]></headline>
<main><![CDATA[TEXT XML DETAILLE]]></main>
<footer><![CDATA[]]></footer>
<files>
<file id="618383" format="pdf" type="Regular Attachment">
<file_headline><![CDATA[Test Attachment]]></file_headline>
<location><![CDATA[http://test.html1796257/618383.pdf]]></location>
</file>
</files>
<location href="/S/151406/1796257.xml"/>
</press_release>
</press_releases>
</body>
</hexML>
i try to get this data : http://test.html1796257/618383.pdf (in the "files" tag)
This is what i tried so far :
string Linkpdf = (from c in DetailXml.Descendants("files")
select c.Element("location").Value).Single();
This returns me the exception mentionned above.
Thanks for your help
If the XML was indented properly :
<files>
<file id="618383" format="pdf" type="Regular Attachment">
<file_headline><![CDATA[Test Attachment]]></file_headline>
<location><![CDATA[http://test.html1796257/618383.pdf]]></location>
</file>
</files>
you'll be able to see clearly that <location> is direct child of <file> element within <files> :
string Linkpdf = (from c in DetailXml.Descendants("files")
select c.Element("file").Element("location").Value).Single();

Removing attributes from elements

I will start off with the code...
private static void File()
{
wixFile = XDocument.Load(filePath);
var fileElements = from file in wixFile.Descendants(GetWixNamespace() + "File")
select file;
foreach (var file in fileElements)
{
if(file.Attributes("Name").Any())
file.Attribute("Name").Remove();
}
wixFile.Save(filePath);
}
I actually check for quite a few attributes in the list and remove or correct them, but just to keep it short I took everything out except for one example of what I was doing. The problem that I am having is that this code is not removing the Name attribute from anything like I would expect it. When I put a break point in and watch it the "file" is getting updated as expected, but the changes are not going over to the wixFile when I save (or during the editing either). I am not entirely sure what I am missing here so any help would be greatly appreciated.
EDIT:
Here is a snippet of the code that is being changed:
<?define ApplicationPath=\\dwdata\develope\DocuWare\DW5\Mast_dsk\?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Directory Id="DATADIR" Name="data">
<Directory Id="mysql" Name="mysql">
<Component Id="procs_priv.MYI" Guid="{1148181A-4818-434F-B2D1-E4B417586168}">
<File Id="procs_priv.MYI" Name="procs_priv.MYI" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\procs_priv.MYI" />
</Component>
<Component Id="procs_priv.MYD" Guid="{A6688F48-71AF-4242-B6D0-CA69452A01B4}">
<File Id="procs_priv.MYD" Name="procs_priv.MYD" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\procs_priv.MYD" />
</Component>
<Component Id="procs_priv.frm" Guid="{3025C26C-8DFF-43D4-A62A-79E78D2D807D}">
<File Id="procs_priv.frm" Name="procs_priv.frm" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\procs_priv.frm" />
</Component>
<Component Id="proc.MYI" Guid="{FD4AA2E1-E059-4549-AE61-222878185A0A}">
<File Id="proc.MYI" Name="proc.MYI" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\proc.MYI" />
</Component>
<Component Id="proc.MYD" Guid="{12EE6EE8-AC44-4601-84C5-14B27CF9A3E6}">
<File Id="proc.MYD" Name="proc.MYD" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\proc.MYD" />
</Component>
<Component Id="proc.frm" Guid="{8A6F2928-5484-4B55-B75F-8475C684A091}">
<File Id="proc.frm" Name="proc.frm" KeyPath="yes" Source="$(var.ApplicationPath)Common\MySql\Server\data\mysql\proc.frm" />
</Component>
</Directory>
</Directory>
</Wix>
I am getting the exact same thing back as well. What I am trying to do is remove the "Name" attribute, but it just isn't working for whatever reason. The GetWiXNamespace() method returns the same namespace that is listed in the wix element as an XNamespace.
I'm going to read between the lines here and guess you are converting from an old version of WiX ( say 2.0 ) that required a File#Name attribute to a version ( say 3.0-3.6 ) that can infer this attribute and doesn't require it.
Here's some code that I just whipped up that I know works assuming the source xml is WiX 3.x. I'm not sure what is wrong with your code but perhaps the method that returns the namespace hasn't accounted for the url change.
XNamespace ns = "http://schemas.microsoft.com/wix/2006/wi";
var doc = XDocument.Load(#"C:\before.wxs");
var elements = from element in doc.Descendants( ns + "File")
where element.Attribute("Name") != null
select element;
foreach (var element in elements)
{
element.Attribute("Name").Remove();
}
doc.Save(#"C:\after.wxs");
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define SourceDir="."?>
<Module Id="YourModuleHere" Language="1033" Version="1.0.0.0">
<Package Id="31c7722e-c2a0-4328-92a2-eacd443c10a9" Manufacturer="YourCompanyHere" InstallerVersion="200" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Component Id="owc7CEF9B0C53324FC23404D2AAAB5D12B8" Guid="6898e947-fa51-8efb-bda4-2e256dea8ed1">
<File Id="owf7CEF9B0C53324FC23404D2AAAB5D12B8" Name="bfsvc.exe" Source="$(var.SourceDir)\Windows\bfsvc.exe" KeyPath="yes" />
</Component>
<Component Id="owc8C6EA26177072C0006EEF8265FEF72A4" Guid="91737806-0b20-24ad-9653-cca05b5778fb">
<File Id="owf8C6EA26177072C0006EEF8265FEF72A4" Name="explorer.exe" Source="$(var.SourceDir)\Windows\explorer.exe" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Module>
</Wix>

Categories