I have built two XML files that map the content of a given folder:
<root>
<folder name="C:\a\b" permision="yes" folderCount="1">
<folders>
<folder name="C:\a\b\c" permision="yes" folderCount="1">
<folders>
<folder name="C:\a\b\c\e" permision="yes" folderCount="0">
<folders/>
<files>
<file name="401-1.htm"/>
<file name="401-2.htm"/>
<file name="401-3.htm"/>
</files>
</folder>
<folder name="C:\a\b\d" permision="yes" folderCount="0">
<folders/>
<files>
<file name="401-4.htm"/>
<file name="401-5.htm"/>
<file name="401-3.htm"/>
</files>
</folder>
</folders>
<files/>
</folder>
</root>
I'd like to know if there is a way to find the difference between the files.
(One file is the old state and the second is the new state, and it's only possible to add files and not remove them. It would be great to remove identical nodes from the new state so only the new files will be left).
I would use LINQ to XML like the project below:
Diff in XML files with LINQ:
http://www.codeproject.com/KB/linq/LinqDiff.aspx
If you'd like to do it in code, you could use Microsoft'ss XML Diff and Patch GUI Tool, and although there isn't a great deal of documentation, there is enough that you should be able to easily diff two XML files in code, in a fairly short space of time. I use it in a couple of projects as part of a series of unit tests which ensure that XML files are being generated correctly.
If you just want to view the differences between the two files, then you could just use any decent diff tool.
Related
I have a nuget package that uses the Apose.PDF package which I have a license for. The license is put in a separate file called Aspose.Total.lic and is located in the same folder The folder structure is like this.
Project
-PDFReader.cs
-Aspose.Total.lic
The PDFReader.cs has the following code to read the license:
static PDFReader()
{
var license = new License();
license.SetLicense("Aspose.Total.lic");
}
And all this works fine locally. But when I export my code to a Nuget package and use the package from another program, I get exceptions that it cannot find "Aspose.Total.lic" Copying the contents of the file and putting it as argument for SetLicense does not work, it expects a file. Now the question is, how and maybe where do I export the file when packing a nuget package? This is my nuspec file (some code is abbreviated):
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
<metadata>
<id>XYZ</id>
<version>1.3.7</version>
<summary />
<dependencies>
<dependency id="Aspose.PDF" version="18.10.0" />
</dependencies>
<frameworkAssemblies>
<frameworkAssembly assemblyName="System" />
<frameworkAssembly assemblyName="System.Data" />
</frameworkAssemblies>
</metadata>
<files>
<file src="XYZ\bin\Release\XYZ.dll" target="lib\net47\XYZ.dll" />
<file src="XYZ\Aspose.Total.lic" target="lib\Aspose.Total.lic" />
</files>
</package>
My guess here is the the target location is wrong for the file.
NuGet exposes 3 folder automatically, lib for dlls, tools for powershell scripts and content for other content. (I think - it's been a while)
Try changing :
<files>
<file src="XYZ\bin\Release\XYZ.dll" target="lib\net47\XYZ.dll" />
<file src="XYZ\Aspose.Total.lic" target="lib\Aspose.Total.lic" />
</files>
To :
<files>
<file src="XYZ\bin\Release\XYZ.dll" target="lib\net47\XYZ.dll" />
<file src="XYZ\Aspose.Total.lic" target="content\Aspose.Total.lic" />
</files>
I think you should package that file as content. You can check the documentation how to achive that.
I'm trying to create an installer using WiX. To includes DLLs into .msi package I tryied two different ways. One of these is:
<DirectoryRef Id="SETTINGSDIR">
<Component Id="CMP_CopySettings" Guid="AC7D1AA1-798B-48F5-AF8D-188B1050D47C" KeyPath="yes">
<CreateFolder />
<File Id="DBA.bat" Source="$(var.SolutionDir)\scr\A\A.WindowsService\bin\$(var.Configuration)\Settings\A_DB clear.bat" Checksum="yes"/>
<File Id="AConfiguration.xml" Source="$(var.SolutionDir)\scr\A\A.WindowsService\bin\$(var.Configuration)\Settings\AConfiguration.xml" Checksum="yes"/>
<File Id="ADB.CE.DEFAULT.sdf" Source="$(var.SolutionDir)\scr\A\A.WindowsService\bin\$(var.Configuration)\Settings\ADB.CE.DEFAULT.sdf" Checksum="yes"/>
<File Id="ADB.CE.sdf" Source="$(var.SolutionDir)\scr\A\A.WindowsService\bin\$(var.Configuration)\Settings\A.CE.sdf" Checksum="yes"/>
<RemoveFile Id="RemoveFileSettings" Name="*" On="uninstall"/>
</Component>
</DirectoryRef>
But as you can easily understand, it's very hard write an xml node for each DLL (6 projects with 200+ DLLs for each one).
The second one is faster, but WiX just creates a link to the folder instead of copy DLLs into msi package
<DirectoryRef Id="SETTINGSDIR">
<Component Id="CMP_CopySettings" Guid="AC7D1AA1-798B-48F5-AF8D-188B1050D47C" KeyPath="yes">
<CreateFolder />
<CopyFile Id="SettingsID" SourceProperty="SETTINGSSOURCEDIRECTORY" DestinationDirectory="SETTINGSDIR" SourceName="*" />
<RemoveFile Id="RemoveFileSettings" Name="*" On="uninstall"/>
</Component>
</DirectoryRef>
Is there a quick solution that can I add at my second way or I have to use heat.exe tool? In this case, can you explain me how to use it? The official documentation is very poor
Thanks
What you want is an harvest tool to do this for you. Luckily it already exists: Heat
In your specific case you might want to use the command heat dir ".\My Files" -gg -g1 -directoryid "YourDirectoryId" -sfrag -template:fragment -out directory.wxs but check what is exactly your need, which harvesting you want to skip etc...
Note the -t <xsl> switch which gives you the total control on how you want to tune the final output.
I'm using MSBuild to manipulate my Project (.csproj) file to update a reference to a static file. The static file will be built by my CI Server (TeamCity) and then the reference the Project uses will need to be updated before the Project itself is built.
Here is an example of the Xml from my csproj file (full version):
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="12.0">
<ItemGroup>
<Content Include="packages\pMixins.0.1.7.nupkg">
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
I have written an MSBuild Task:
<Target Name="ReplaceNugetPackageDependency" BeforeTargets="PrepareForBuild" >
<XmlPoke
XmlInputPath="$(MSBuildProjectFile)"
Query="//n:Project/n:ItemGroup/
n:Content[starts-with(#Include, 'packages')]/#Include"
Value="TEST-TEST"
Namespaces="<Namespace Prefix='n'
Uri='http://schemas.microsoft.com/developer/msbuild/2003'
Name='DoNotKnowWhatThisIsFor-ButItIsRequired' />" >
</XmlPoke>
</Target>
But when I run it I get the message 0 replacements.
So I added an XmlPeek task to test the query:
<XmlPeek
XmlInputPath="$(MSBuildProjectFile)"
Query="/n:Project/n:ItemGroup/
n:Content[starts-with(#Include, 'packages')]/#Include"
Namespaces="<Namespace Prefix='n'
Uri='http://schemas.microsoft.com/developer/msbuild/2003'
Name='DoNotKnowWhatThisIsFor-ButItIsRequired' />">
<Output TaskParameter="Result" ItemName="Peeked" />
</XmlPeek>
<Message Text="Text: #(Peeked)"/>
When I run MSBuild XmlPeek is able to read the Xml:
Text: packages\pMixins.0.1.7.nupkg
The queries are exactly the same! Why can't XmlPoke manipulate the Xml if XmlPeek can read it?
UPDATE
After hours of playing with this, I finally found an XPath query that will get XmlPoke to do what I want:
Query="//n:Project/n:ItemGroup/
n:Content[starts-with(#Include, 'packages')]/n:IncludeInVSIX/../#Include"
Why is it necessary to add /n:IncludeInVSIX/..? Is this a bug??
Just wanted to confirm for anyone else who encounters this, that this is how, in fact, you get around the issue of not being able to use the same exact XPath query in XmlPeek task and XmlPoke task.
Original query to replace "file" attribute value of AppSettings element in regular web.config:
<appSettings file="devsettings.config">
<add key="BuildVersion" value="" />
</appSettings>
To get at the "file" attribute in XmlPeek task I used following XPath query:
//appSettings[#file='devsettings.config']/#file
However this same query wouldn't work in XmlPoke task. Instead the following worked like what #philip-pittle discovered in his update to the question
//appSettings[#file='devsettings.config']/add/../#file
<XmlPeek XmlInputPath="$(_BuildPath)web.config"
Query="//appSettings[#file='devsettings.config']/#file">
<Output TaskParameter="Result" ItemName="ExistingPeeked" />
</XmlPeek>
<XmlPoke XmlInputPath="$(_BuildPath)web.config"
Query="//appSettings[#file='devsettings.config']/add/../#file"
Value="$(_EnvironmentConfig)" />
This was using following versions of MSBuild.
Microsoft (R) Build Engine version 12.0.31101.0
[Microsoft .NET Framework, version 4.0.30319.18444]
May be it's a bug? But definitely odd behavior.
For a xml Document like below I display all subfolders and files for the currently selected folder. For that from the xml string i remove the unmatched folders and built a UI using XSLT on the front end. Seems the operation is removing all the nodes that makes resulting string invalid xml. so
How do i remove elements from xml using Linq to Xml without changing the validity of the document
Xml Document
<?xml version="1.0" encoding="utf-8"?>
<Folder>
<Folders>
<Folder ID="1" Name="Root" ParentId="0">
<Files></Files>
</Folder>
<Folder ID="2" Name="My Documents" ParentId="1">
<Files>
<File Name="LicenceCode.txt" Size="2000" CreatedOn="1/1/2012 12:12:00 PM" CreatedBy="1" ModifiedOn="1/10/2012 10:12:56 AM" ModifiedBy="2"></File>
</Files>
</Folder>
</Folders>
</Folder>
Code
XElement filesAndFolders = XElement.Parse(xmlDocumentString);
string outputFolders = string.concat(from folders in filesAndFolders in filesAndFolder.Elements("Folder").Folder("Folders") where folders.Attribute("ParentId").Value.Equals(selectedFolderId) select folders);
//pass outputFolders string to xsl to build the UI
Problem
The outputFolders string is invalid as it contains only below string not a valid document
<Folder ID="2" Name="My Documents" ParentId="1">
<Files>
<File Name="LicenceCode.txt" Size="2000" CreatedOn="1/1/2012 12:12:00 PM" CreatedBy="1" ModifiedOn="1/10/2012 10:12:56 AM" ModifiedBy="2"></File>
</Files>
</Folder>
How about removing the elements you don't want:
var doc = XDocument.Parse(xmlString);
doc.Element("Folder").Element("Folders").Elements("Folder").Where(f => f.Attribute("ID").Value == "1").Remove();
I am new to xml and unable to find a way to get content in between tags.
My XML file is
<?xml version="1.0" encoding="utf-8"?>
<block1>
<file name="c:\w0.xml">
<word>Text</word>
<number>67</number>
</file>
<file name="c:\w1.xml">
<word>Text</word>
<number>67</number>
</file>
<file name="c:\w2.xml">
<word>Text</word>
<number>67</number>
</file>
</block1>
LINQ to XML is a great place to start. Consider the following code to parse your XML.
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<block1>
<file name=""c:\w0.xml"">
<word>Text</word>
<number>67</number>
</file>
<file name=""c:\w1.xml"">
<word>Text</word>
<number>67</number>
</file>
<file name=""c:\w2.xml"">
<word>Text</word>
<number>67</number>
</file>
</block1>";
XDocument document = XDocument.Parse(xml);
var block = from file in document.Descendants("file")
select new
{
Name = file.Attribute ("name").Value,
Word = file.Element("word").Value,
Number = (int)file.Element("number")
};
foreach (var file in block)
{
Console.WriteLine("{0}\t{1}\t{2}", file.Name, file.Word, file.Number );
}
You can, of course, load the XML directly from a file using XDocument.Load instead of using Parse to read an XML string. XDocument is in the System.Xml.Linq namespace. Frankly, I would start there, but there are other options to work with XML in the System.Xml namespace (XmlReader.Create, etc.).
You need to use an XML Query language. I would recommend LINQ to XML if you're using .Net 3.5 or XPath if you're using something earlier. XPath has the advantage of being an industry standard, but LINQ to XML is a much 'cleaner' API, in my opinion.
How to query XML with an XPath expression by using Visual C# - Tutorial on using XPath
LINQ to XML Video Tutorial
MSDN XPath Examples - From the XPath Reference
Location Paths - Includes text() function for example.