Search code examples
c#linqjson.netfiletreedirectory-tree

Creating A Filetree from a directory with Json.net LINQ


With the following code:

static void Main(string[] args)
    {
        JObject fileSystemTree = CreateFileSystemJsonTree("C:/DirectoryTree");
        Console.WriteLine(fileSystemTree);
        Console.WriteLine("------");
        //
        //  Write it out to a file
        //
        System.IO.File.WriteAllText(@"C:\jsontree.txt", fileSystemTree.ToString());
        Console.ReadLine();
    }
    static JObject joNew = new JObject();
    static JObject CreateFileSystemJsonTree(string source)
    {
        //
        //  Build a list of all the IPs
        //
        //Console.WriteLine(source);
        using (var poiDbContext = new poiDbEntities())
        {
            DirectoryInfo di = new DirectoryInfo(source);
                {
                    joNew = new JObject(
                        new JProperty(di.Name, new JArray(Directory.GetDirectories(source).Select(d => CreateFileSystemJsonTree(d)))),
                        new JProperty("files", new JArray(di.GetFiles().Select(fi => new JObject(new JProperty(fi.Name, GetText(fi.Name)))))));
                }
                Console.WriteLine(joNew);
        }
        return joNew;
    }
    public static string GetText(string fiName)
    {
        using (var poiDbContext = new poiDbEntities())
        {
            string indexNameBody = fiName.Substring(0, fiName.LastIndexOf('.'));
            var indexResult = "test";  // dummied up for use by evaluators 
            return indexResult.Trim();
        }
    }

I am trying to create a filetree from a system directory using recursion. I have this working with XML but would prefer a JSON tree. The problem is that the txt files appear, not withing the Parent's [], which represents the lowest folder, but instead being added as JsonProperties with the text name I have added as "files"(which I don't want). Plus the "files" are generated even if there are no files, such as in an empty folder. The system generated snippet is followed by the a possible desired snippet.

"Sports": [
        {
          "NBA": [],
          "files": []  // Undesirable--The folder is empty`` 
        },``

The two snippets:

{  
      "Politics": [  
        {  
          "PresCandidates": [  
            {  
              "Republican": [],   // Notice the files are not in array   within the [] of the Republican    
              "files": [    
                {  
                  "carson_ben_s.txt": "Ben Carson"  
                },  
                {  
                  "trump_donald_j.txt": "Donald Trump"  
                },  
                {
                  "walker_scott_k.txt": "Scott Walker"  
                }  
              ]  
            }  
          ]  
        }  
      ],  
      "Politics": [  // The desired format
        {  
          "PresCandidates": [  
            {  
              "Republican": [  
                                {  
                                    "carson_ben_s.txt": "Ben Carson"  
                                },  
                                {  
                                    "trump_donald_j.txt": "Donald Trump"  
                                },  
                                {  
                                    "walker_scott_k.txt": "Scott Walker"  
                                }  
                            ],  

{


Solution

  • I'm not sure I understand the JSON you want to create. If you're looking for a set of nested JSON objects where each property name corresponds to the file or directory name, and for a file, the property value is given by some callback method, while for a subdirectory, the value is an object recursively containing objects for all contents of the subdirectory, you could do this:

    public static class DirectoryInfoExtensions
    {
        public static JObject ToJson<TResult>(this DirectoryInfo info, Func<FileInfo, TResult> getData)
        {
            return new JObject
                (
                info.GetFiles().Select(f => new JProperty(f.Name, getData(f))).Concat(info.GetDirectories().Select(d => new JProperty(d.Name, d.ToJson(getData))))
                );
        }
    }
    

    And then use it like:

            string path = @"C:\Program Files (x86)\Microsoft Visual Studio 9.0";
    
            var di = new DirectoryInfo(path);
            Debug.WriteLine(di.ToJson(f => f.LastWriteTimeUtc));
    

    Which produces output like:

    {
      "redist.txt": "2007-10-16T21:56:34Z",
      "Common7": {
        "IDE": {
          "Brief.vsk": "2007-06-20T21:55:14Z",
          "WinFxCustomControlTemplateWizard.dll": "2008-07-30T14:06:58Z",
          "1033": {
            "cmddefui.dll": "2008-07-30T14:06:58Z",
            "Microsoft.VisualStudio.DesignUI.dll": "2008-07-30T14:06:58Z",
            "Microsoft.VisualStudio.EditorsUI.dll": "2008-07-30T14:06:58Z",
    
         Many values removed,
    
            "VsWizUI.dll": "2008-07-30T14:06:58Z",
            "WindowsFormsIntegration.PackageUI.dll": "2008-07-30T14:06:58Z"
          },
          "en": {
            "Microsoft.VisualStudio.Package.LanguageService.xml": "2007-03-02T04:30:40Z",
            "Microsoft.VisualStudio.Shell.Design.xml": "2007-03-06T04:40:44Z",
            "Microsoft.VisualStudio.Shell.xml": "2007-03-06T04:40:44Z"
          },
          "ExceptionAssistantContent": {
            "1033": {
              "DefaultContent.xml": "2007-09-03T05:11:44Z"
            }
          }
        }
      }
    }
    

    Is that what you want? Your partial JSON sample has arrays but I don't see where or how you need them.

    If this is not what you want, could you clarify the desired JSON format somewhat?

    By the way, if you have XML working, you could always use JsonConvert.SerializeXNode().