here is the problem: take a relative folder structure on Windows, package it in a JSON file, and replicate it on a linux distro, say Ubuntu 20.04, using .NET 5.
Here's my (very much simplified) approach:
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
using Internal;
public class FileData
{
public string Path { get; set; }
public string FileName { get; set; }
}
public static void Main(string[] args)
{
var rootPath = Path.GetFullPath(@"C:/tempFolder");
var files = Directory.GetFiles(rootPath, "*.*", SearchOption.AllDirectories);
List<FileData> fileData = new List<FileData>();
foreach (var file in files)
{
fileData.Add(new FileData
{
FileName = Path.GetFileName(file),
Path = Path.GetRelativePath(rootPath, Path.GetDirectoryName(file))
});
}
Console.WriteLine(JsonConvert.SerializeObject(fileData));
}
Now this outputs something like
[
{
"Path":"InnerFolder\Subfolder",
"FileName":"TestFile.txt"
}
]
Writing the JSON to a file and transferring it to linux, and executing something along the lines of:
var rootPath = Path.GetFullPath("/home/user/tempFolder2/");
var fileData = JsonConvert.DeserializeObject<List<FileData>>(dataReadFromFile);
foreach(var file in fileData)
{
var path = Path.Combine(rootPath, file.Path, file.FileName);
//...proceed to create file and path
}
The problem is, the path here returns /home/user/tempFolder2/InnerFolderSubfolder/TestFile
, as a backslash is a valid path character in linux and it does not get parsed by the Path.Combine.
I figured two options here:
- Somehow force windows to use the AltPathSeparatorChar ('/')
- Go the poor man's way and simply replace all "\" and '' occurences with / in relative paths, if the system is not WinNT32.
Now I severely dislike the second option, in case the paths are generated using linux and a backslash is indeed a backslash. I would much prefer all output files have a standard format for the "Path" variables, but I cannot for the life of me figure out, how to force the Path namespace to use forward slashes for paths instead of backslashes in Windows.
Is there a way achieving this using the Path api or will I have to go the if(OS)... way?
EDIT: After a short debate with @Olivier Rogier, I've decided to update the problem, in case someone knows of an integrated solution.
What it boils down to:
var relativePath = Path.GetRelativePath("./folder", "./folder/sf1/sf2");
returns "sf1sf2" in Windows and "sf1/sf2" in Linux. So, without additional code on the programmer's end, the line of code is non-deterministic, if the code is run on the two different systems.
Running a simple example like the one here
//WRITER
var relativePath = Path.GetRelativePath("./folder", "./folder/sf1/sf2");
File.WriteAllText("transferFile", relativePath);
//READER, on another OS
var relativePath = File.ReadAllText("transferFile");
var combinedPath = Path.Combine("./folder", relativePath);
does not produce the same result in both environments.
1: Writer: WIN, Reader: WIN Writer output: sf1\sf2 Combined path in reader: folder\sf1\sf2 Dir create result: -folder -sf1 -sf2
2: Writer: LNX, Reader: WIN Writer output: sf1/sf2 Combined path in reader: folder\sf1\sf2 Dir create result: -folder -sf1 -sf2
3: Writer: WIN, Reader: LNX Writer output: sf1\sf2 Combined path in reader: folder/sf1\sf2/ Dir create result: -folder -sf1\sf2
Another example is using the Linux directory path
-folder -sf1stillavalidfolde name
Writer: LNX, Reader: WIN Writer output: sf1stillavalidfolde name Combined path in reader: folder\sf1\stillavalidfolde\rname Dir create result: -folder -sf1 -stillavalidfolde -rname
So far, the only viable solution is something along the lines of
//WRITER
var relativePath = Path.GetRelativePath("./folder", "./folder/sf1/sf2");
File.WriteAllLines("transferFile", relativePath.Split(Path.DirectorySeparatorChar));
//READER, on another OS
var relativePaths = File.ReadAllLines("transferFile");
var combinedPath = Path.Combine("./folder", relativePaths);
This of course means updates to all serializers, serialization code an so on, so they use the split/join method anywhere the path is serialized, and less human readability.
One would expect at least an overload for the Path namespace methods, where one can either specify a directory separation character (forcing the Path.GetFullPath and Path.Combine methods to always use the same format), a RelativePath class, where this is handled internally, or perhaps another way I haven't thought of.
To reiterate, is there a way to extract, store, and read relative pathnames in strings than can be both read and written on Windows and Linux, without converting the string path to an array of directories?
See Question&Answers more detail:os