![]() |
VOOZH | about |
dotnet add package DoenaSoft.FolderList --version 1.0.4
NuGet\Install-Package DoenaSoft.FolderList -Version 1.0.4
<PackageReference Include="DoenaSoft.FolderList" Version="1.0.4" />
<PackageVersion Include="DoenaSoft.FolderList" Version="1.0.4" />Directory.Packages.props
<PackageReference Include="DoenaSoft.FolderList" />Project file
paket add DoenaSoft.FolderList --version 1.0.4
#r "nuget: DoenaSoft.FolderList, 1.0.4"
#:package DoenaSoft.FolderList@1.0.4
#addin nuget:?package=DoenaSoft.FolderList&version=1.0.4Install as a Cake Addin
#tool nuget:?package=DoenaSoft.FolderList&version=1.0.4Install as a Cake Tool
A .NET library for scanning directories and generating hierarchical XML representations of folder structures based on file search patterns.
DoenaSoft.FolderList is a core library that provides functionality to scan file systems, locate files matching specific patterns, and generate structured XML output representing the directory hierarchy. The library works directly with the .NET file system APIs for efficient directory scanning and file pattern matching.
IFolderConsolidator to group multi-disc/multi-part foldersIBackupStrategy interfaceIPathTransformer interfaceInstall via NuGet Package Manager:
dotnet add package DoenaSoft.FolderList
Or via Package Manager Console:
Install-Package DoenaSoft.FolderList
using DoenaSoft.FolderList;
// Get a folder reference
var rootFolder = new DirectoryInfo(@"C:\Videos");
// Create scanner with default settings (no consolidation or transformation)
var creator = new Creator();
// Scan for files matching patterns and generate XML output
var (oldFileName, outFileName) = creator.Scan(
folder: rootFolder,
searchPatterns: "*.mp4,*.mkv,*.avi",
outputFileName: @"C:\Output\video-list.xml"
);
// oldFileName contains the previous version path (if it existed)
// outFileName contains the new output file path
You can provide custom path transformation logic for special use cases:
using DoenaSoft.FolderList;
// Create a custom transformer in your application
public class NetworkPathTransformer : IPathTransformer
{
public string Transform(string fullPath)
{
if (string.IsNullOrEmpty(fullPath))
return fullPath;
if (fullPath.StartsWith(@"N:\", StringComparison.InvariantCultureIgnoreCase))
{
return fullPath.Substring(3).Replace("\\", "/").TrimEnd('/') + "/";
}
return fullPath;
}
}
// Use it when scanning
var pathTransformer = new NetworkPathTransformer();
var creator = new Creator(pathTransformer: pathTransformer);
var rootFolder = new DirectoryInfo(@"N:\Videos");
var (oldFileName, outFileName) = creator.Scan(
folder: rootFolder,
searchPatterns: "*.mp4,*.mkv,*.avi",
outputFileName: @"C:\Output\video-list.xml"
);
You can implement custom logic to consolidate multi-disc or multi-part folders:
using DoenaSoft.FolderList;
// Create a custom folder consolidator
public class MultiDiscFolderConsolidator : IFolderConsolidator
{
public bool ShouldConsolidate(DirectoryInfo folder)
{
if (folder == null)
return false;
var name = folder.Name;
return name.StartsWith("cd", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("disc", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("part", StringComparison.OrdinalIgnoreCase);
}
}
// Use both consolidator and transformer
var folderConsolidator = new MultiDiscFolderConsolidator();
var pathTransformer = new NetworkPathTransformer();
var creator = new Creator(folderConsolidator, pathTransformer);
var (oldFileName, outFileName) = creator.Scan(
folder: new DirectoryInfo(@"C:\Music"),
searchPatterns: "*.mp3,*.flac",
outputFileName: "music-list.xml"
);
You can implement custom backup file management strategies:
using DoenaSoft.FolderList;
// Create a custom backup strategy
public class TwoLevelBackupStrategy : IBackupStrategy
{
public string CreateBackups(string outputFilePath)
{
if (string.IsNullOrEmpty(outputFilePath))
return null;
var oldOldFileName = $"{outputFilePath}.old.old";
var oldFileName = $"{outputFilePath}.old";
// Move .old to .old.old
if (File.Exists(oldFileName))
{
if (File.Exists(oldOldFileName))
File.Delete(oldOldFileName);
File.Move(oldFileName, oldOldFileName);
}
// Move current to .old
if (File.Exists(outputFilePath))
{
File.Move(outputFilePath, oldFileName);
return oldFileName;
}
return null;
}
}
// Use all three customizations
var folderConsolidator = new MultiDiscFolderConsolidator();
var pathTransformer = new NetworkPathTransformer();
var backupStrategy = new TwoLevelBackupStrategy();
var creator = new Creator(folderConsolidator, pathTransformer, backupStrategy);
var (oldFileName, outFileName) = creator.Scan(
folder: new DirectoryInfo(@"C:\Videos"),
searchPatterns: "*.mp4,*.mkv",
outputFileName: "video-list.xml"
);
// Or create other custom transformers for different scenarios
public class UncPathTransformer : IPathTransformer
{
public string Transform(string fullPath)
{
// Your custom transformation logic
return fullPath.Replace(@"\\server\share", "/share");
}
}
For more control over the scanning process, you can use the individual components:
using DoenaSoft.FolderList;
var rootFolder = new DirectoryInfo(@"C:\Music");
// Create dependencies
var folderConsolidator = new MultiDiscFolderConsolidator();
var pathTransformer = new NetworkPathTransformer();
var backupStrategy = new TwoLevelBackupStrategy();
// Step 1: Get all folders containing matching files
var folderGetter = new FolderGetter(folderConsolidator);
var folderData = folderGetter.Get(rootFolder, "*.mp3,*.flac");
// Step 2: Create XML structure
var xmlCreator = new XmlCreator();
var rootItem = xmlCreator.Create(rootFolder, folderData);
// Step 3: Clean up the structure (remove redundant entries)
var cleaner = new Cleaner(pathTransformer);
cleaner.Clean(rootItem);
// Step 4: Serialize to XML file
var serializer = new Serializer(backupStrategy);
var (oldFile, newFile) = serializer.Serialize(
rootFolder,
"music-list.xml",
rootItem
);
Main entry point for the scanning process. Orchestrates the entire workflow from scanning to serialization.
Namespace: DoenaSoft.FolderList
public sealed class Creator
{
public Creator(
IFolderConsolidator folderConsolidator = null,
IPathTransformer pathTransformer = null,
IBackupStrategy backupStrategy = null);
public (string oldFileName, string outFileName) Scan(
DirectoryInfo folder,
string searchPatterns,
string outputFileName);
}
Constructor Parameters:
folderConsolidator: (Optional) Custom folder consolidator for grouping multi-disc/multi-part folderspathTransformer: (Optional) Custom path transformer for specialized path handlingbackupStrategy: (Optional) Custom backup strategy for managing backup filesScan Method Parameters:
folder: The root directory to scan recursivelysearchPatterns: Comma-separated file patterns (e.g., ".mp4,.mkv,*.avi")outputFileName: The output XML file name (can be relative or absolute path)Returns: A tuple with the backup file path and the new output file path
Scans directories and collects folder information based on file patterns. Handles the recursive file discovery and filters folders containing matching files.
Namespace: DoenaSoft.FolderList
internal sealed class FolderGetter
{
internal FolderGetter(IFolderConsolidator folderConsolidator = null);
internal List<FolderData> Get(
DirectoryInfo folder,
string searchPatterns);
}
Constructor Parameters:
folderConsolidator: (Optional) Determines which folders should be consolidated with their parentKey Behavior:
IFolderConsolidatorGenerates hierarchical XML structure from folder data. Converts the flat list of folders into a nested tree structure.
Namespace: DoenaSoft.FolderList
internal sealed class XmlCreator
{
internal RootItem Create(
DirectoryInfo folder,
List<FolderData> folderDatas);
}
Key Behavior:
Optimizes the XML structure by removing redundant entries and applying optional path transformations.
Namespace: DoenaSoft.FolderList
internal sealed class Cleaner
{
internal Cleaner(IPathTransformer pathTransformer = null);
internal void Clean(RootItem rootItem);
}
Constructor Parameters:
pathTransformer: (Optional) Custom path transformer for specialized path handlingKey Behavior:
IPathTransformer if providedHandles XML serialization to file system with automatic backup management.
Namespace: DoenaSoft.FolderList
internal sealed class Serializer
{
internal Serializer(IBackupStrategy backupStrategy = null);
internal (string oldFileName, string outFileName) Serialize(
DirectoryInfo folder,
string outputFileName,
RootItem rootItem);
}
Constructor Parameters:
backupStrategy: (Optional) Custom backup strategy for managing backup filesKey Behavior:
IBackupStrategy if providedInternal data class representing a folder with timestamp information.
Namespace: DoenaSoft.FolderList
internal sealed class FolderData : IEquatable<FolderData>, IComparable<FolderData>
{
public DirectoryInfo Folder { get; }
public DateTime? LastWriteTime { get; }
}
Key Behavior:
Public interface for implementing custom path transformation logic.
Namespace: DoenaSoft.FolderList
public interface IPathTransformer
{
string Transform(string fullPath);
}
Purpose: Allows callers to inject custom path transformation logic during the cleaning phase, enabling specialized handling of paths (e.g., network drives, UNC paths, cloud storage paths).
Implementation Example:
// Example: Network path transformer for N:\ drives
public sealed class NetworkPathTransformer : IPathTransformer
{
public string Transform(string fullPath)
{
if (string.IsNullOrEmpty(fullPath))
{
return fullPath;
}
if (fullPath.StartsWith(@"N:\", StringComparison.InvariantCultureIgnoreCase))
{
var cleanedPath = fullPath.Substring(3).Replace("\\", "/").TrimEnd('/') + "/";
return cleanedPath;
}
return fullPath;
}
}
This is an example implementation - create your own in your application based on your specific needs.
Public interface for implementing custom folder consolidation logic.
Namespace: DoenaSoft.FolderList
public interface IFolderConsolidator
{
bool ShouldConsolidate(DirectoryInfo folder);
}
Purpose: Allows callers to inject custom logic to determine which folders should be consolidated with their parent folders, enabling specialized handling of multi-disc, multi-part, or other organizational structures.
Implementation Example:
// Example: Multi-disc folder consolidator
public sealed class MultiDiscFolderConsolidator : IFolderConsolidator
{
public bool ShouldConsolidate(DirectoryInfo folder)
{
if (folder == null)
return false;
var name = folder.Name;
return name.StartsWith("cd", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("disc", StringComparison.OrdinalIgnoreCase)
|| name.StartsWith("part", StringComparison.OrdinalIgnoreCase);
}
}
This is an example implementation - create your own in your application based on your specific organizational needs.
Public interface for implementing custom backup file management strategies.
Namespace: DoenaSoft.FolderList
public interface IBackupStrategy
{
string CreateBackups(string outputFilePath);
}
Purpose: Allows callers to inject custom backup file management logic before writing new XML output files. Enables flexible backup strategies such as versioned backups, timestamped backups, or multi-level backup chains.
Implementation Example:
// Example: Two-level backup strategy (.old and .old.old)
public sealed class TwoLevelBackupStrategy : IBackupStrategy
{
public string CreateBackups(string outputFilePath)
{
if (string.IsNullOrEmpty(outputFilePath))
return null;
var oldOldFileName = $"{outputFilePath}.old.old";
var oldFileName = $"{outputFilePath}.old";
// Move .old to .old.old
if (File.Exists(oldFileName))
{
if (File.Exists(oldOldFileName))
File.Delete(oldOldFileName);
File.Move(oldFileName, oldOldFileName);
}
// Move current to .old
if (File.Exists(outputFilePath))
{
File.Move(outputFilePath, oldFileName);
return oldFileName;
}
return null;
}
}
This is an example implementation - create your own based on your backup requirements (e.g., timestamped backups, unlimited versions, cloud storage backups).
The library provides flexible folder consolidation through the IFolderConsolidator interface. Common use cases include:
By implementing IFolderConsolidator, you can define which folders should be grouped under their parent folder to avoid duplicate entries in the output. If no consolidator is provided, all folders are treated independently.
The library generates XML files with the following structure:
<?xml version="1.0" encoding="utf-8"?>
<root>
<item name="Movies">
<item name="Action">
<item name="Movie1" lastWriteTime="2024-01-15T10:30:00Z" />
</item>
</item>
</root>
Key Elements:
<root>: The root element containing all folder items<item>: Represents a folder in the hierarchy
name: The folder name (not full path)lastWriteTime: UTC timestamp of the newest matching file (only on leaf nodes)fullPath: Complete path (only on leaf nodes after cleaning)XSLT Transformation: The XML output is formatted using XSLT for consistent styling and readability.
The library follows a pipeline architecture:
List<FolderData>RootItem (hierarchical tree)RootItem in placeThis separation of concerns makes the code maintainable and testable.
The library includes comprehensive unit tests in the FolderList.Tests project:
All tests use MSTest framework and run on .NET 10.0.
The library lets exceptions bubble up to the caller. Ensure you handle:
DirectoryNotFoundException: When the root folder doesn't existUnauthorizedAccessException: When lacking permissions to access foldersIOException: When file operations fail (e.g., disk full)// Single pattern
Creator.Scan(folder, "*.mp4", "output.xml");
// Multiple patterns
Creator.Scan(folder, "*.mp4,*.mkv,*.avi", "output.xml");
// All files
Creator.Scan(folder, "*.*", "output.xml");
// Specific file types
Creator.Scan(folder, "*.jpg,*.png,*.gif,*.bmp", "images.xml");
Use IPathTransformer when you need to customize how paths appear in the XML output:
// Example: Network path transformer (implement in your application)
public class NetworkPathTransformer : IPathTransformer
{
public string Transform(string fullPath)
{
if (string.IsNullOrEmpty(fullPath))
return fullPath;
if (fullPath.StartsWith(@"N:\", StringComparison.InvariantCultureIgnoreCase))
{
return fullPath.Substring(3).Replace("\\", "/").TrimEnd('/') + "/";
}
return fullPath;
}
}
var transformer = new NetworkPathTransformer();
Creator.Scan(folder, "*.mp4", "output.xml", transformer);
// Custom transformer for UNC paths
public class UncPathTransformer : IPathTransformer
{
public string Transform(string fullPath)
{
if (fullPath.StartsWith(@"\\server\share\"))
{
return fullPath.Replace(@"\\server\share\", "").Replace("\\", "/");
}
return fullPath;
}
}
// Custom transformer for multiple scenarios
public class MultiPathTransformer : IPathTransformer
{
public string Transform(string fullPath)
{
// Remove sensitive server names
fullPath = fullPath.Replace(@"\\internal-server\", "/");
// Normalize cloud storage paths
if (fullPath.Contains("OneDrive"))
{
fullPath = fullPath.Replace("OneDrive - Company", "OneDrive");
}
return fullPath;
}
}
When to use path transformers:
## Dependencies
- [DoenaSoft.FolderList.Xml](https://www.nuget.org/packages/DoenaSoft.FolderList.Xml/) (>= 1.0.5) - XML schema definitions and data models
The library uses `DoenaSoft.ToolBox.Generics.XsltSerializer<T>` (via FolderList.Xml) for XSLT-based XML serialization.
## Target Frameworks
- .NET Standard 2.0
- .NET 10.0
## Version History
### 1.0.0
- Initial release
- Core scanning and XML generation functionality
- Multi-target support for .NET Standard 2.0 and .NET 10.0
- Comprehensive XML documentation for all APIs
- Smart folder consolidation (cd*, disc*, part*)
- Automatic backup management (.old, .old.old)
- XSLT-based XML serialization
## License
This library is licensed under the MIT License.
## Author
DJ Doena / Doena Soft.
## Links
- [GitHub Repository](https://github.com/DJDoena/ListFolders)
- [NuGet Package](https://www.nuget.org/packages/DoenaSoft.FolderList/)
- [Project Homepage](https://github.com/DJDoena/ListFolders)
## Support
For issues, questions, or contributions, please visit the [GitHub Issues](https://github.com/DJDoena/ListFolders/issues) page.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.