2

Currently the code I have below calls JsonToCsv() to parse a JSON file and append to csv accordingly, so the result is as such:

result.csv

File Name    Page  Practice Name
file1.json   1     Associates & Co 

However, as you can see i am using a for loop to iterate over a bunch of JSON files, and my intention is that it parses them and appends them to the csv. so the expected csv file should like like this if i have 3 json files:

File Name    Page  Practice Name
fileXYZ.json 1     XYZ & Co
fileAB2.json 1     ABC & Co
file1.json   1     Associates & Co

however, whats happening is that the csv file is getting overwritten and I only see the results from the last file in the csv file. How do i make it append to the same file?

static void Main(string[] args)
{
    //Output to CSV
    foreach (var jsonFile in Directory.GetFiles(jsonFilesPath))
    {
        JsonToCsv(jsonFile, csvFilePath);
    }
}

public static void JsonToCsv(string jsonInputFile, string csvFile)
{
    using (var p = new ChoJSONReader(jsonInputFile).WithJSONPath("$..readResults"))
    {
        using (var w = new ChoCSVWriter(csvFile)//.WithFirstLineHeader())
            .WithField("FileName", fieldName: "File Name")
            .WithField("Page")
            .WithField("PracticeName", fieldName: "Practice Name")
            .WithFirstLineHeader()
            )
        {
            // Limit the result to page 1 since the fields below only exist on the 1st page
            w.Write(p
                .Where(r1 => r1.page == 1)
                .Select(r1 =>
                {
                    var lines = (dynamic[])r1.lines;
                    return new
                    {
                        FileName = inputFile,
                        Page = r1.page,
                        PracticeName = lines[6].text,
                    };
                }
        ));
        }
    }
}
Yehor Androsov
  • 4,885
  • 2
  • 23
  • 40
Cataster
  • 3,081
  • 5
  • 32
  • 79
  • Does this answer your question? [C#: Appending \*contents\* of one text file to another text file](https://stackoverflow.com/questions/5096194/c-appending-contents-of-one-text-file-to-another-text-file) – DCCoder Nov 12 '20 at 06:00
  • @DCCoder no that's different. His is more complicated scenario and a different use case. Also hes not using a function in a loop. Lastly I did consider AppendAllText. However, that is applicable to IO stream, but I am using choetl which idk what the equivalent for that is, if even that is the solution... – Cataster Nov 12 '20 at 06:05
  • So AppendAllText and using a FileStream append won't suit your needs? (Both of which are covered in the answers in that question). Can you explain why you are unable to use either of these methods? – DCCoder Nov 12 '20 at 06:07
  • @DCCoder oh i see now how that can be done with Yegor's answer. `FileMode.Append` . – Cataster Nov 12 '20 at 06:48

1 Answers1

3

1st option. I'd recommend you to alter your method signature to accept multiple files.

public static void JsonToCsv(IEnumerable<string> jsonInputFiles, string csvFile)
{
    using (var w = new ChoCSVWriter(csvFile)
        .WithField("FileName", fieldName: "File Name")
        .WithField("Page")
        .WithField("PracticeName", fieldName: "Practice Name")
        .WithFirstLineHeader()
        )
    {
        foreach (var jsonInputFile in jsonInputFiles)
        {
            using (var p = new ChoJSONReader(jsonInputFile).WithJSONPath("$..readResults"))
            {
                w.Write(p
                    .Where(r1 => r1.page == 1)
                    .Select(r1 =>
                    {
                        var lines = (dynamic[])r1.lines;
                        return new
                        {
                            FileName = inputFile,
                            Page = r1.page,
                            PracticeName = lines[6].text,
                        };
                    }
                ));
            }
        }
    }
}

2nd option. Use FileStream with Append option, and do some extra code to handle different cases, see comments.

using (var fs = new FileStream(@"D:\file.csv", FileMode.Append, FileAccess.Write))
{
    using (var writer = new ChoCSVWriter(fs))
    {
        writer.WithField("FileName", fieldName: "File Name")
            .WithField("Page")
            .WithField("PracticeName", fieldName: "Practice Name");

        if (fs.Position == 0) // we don't need header if file already existed before
        {
            writer.WithFirstLineHeader();
        }

        writer.Write(new
        {
            FileName = "Test",
            Page = 1,
            PracticeName = "Abc"
        });

    }

    fs.Write(Environment.NewLine); // append new line carrier so we don't write to the same line when file reopened for writing
}

3rd option - from comments (based on 2nd). Switch between create and append options

bool someCondition = true;
FileMode fileMode = someCondition ? FileMode.Append : FileMode.Create;
using (var fs = new FileStream(@"D:\file.csv", fileMode, FileAccess.Write))
Yehor Androsov
  • 4,885
  • 2
  • 23
  • 40
  • Interesting! So one thing I realized is that chocsvwriter nicely creates the csv file automatically if it doesn't exist. However, what if I want to append, like your options allow, but whenever a new session is initiated it creates (overwrites) existing file (if any)? That would be a nice 3rd option – Cataster Nov 12 '20 at 06:09
  • 1
    just pass a boolean flag that will flip FileMode.Append to FileMode.Create – Yehor Androsov Nov 12 '20 at 06:11
  • I edited my answer, please check. `someCondition` can be passed through parameters – Yehor Androsov Nov 12 '20 at 06:15
  • i just tried out the 2nd option and it works amazing! however, i had to get rid of the `?` from `writer?.Dispose();` in the finally block because otherwise it was appending very strangely. – Cataster Nov 12 '20 at 06:43
  • it is confusing, `?` is just a null check. I don't think it can affect csv writing – Yehor Androsov Nov 12 '20 at 06:50
  • very strange, what was happening is it was appending the same file twice. however, when i got rid of it, it appended the other file information as i had expected. is it maybe cause i wrappend the 2nd option around another using, `using (var p = new ChoJSONReader(jsonInputFile).WithJSONPath("$..readResults")) // "readResults": [ {` so i can get the advantage of my old code `writer.Write(p .Where(r1 => r1.page == 1) .Select(r1 =>....` – Cataster Nov 12 '20 at 06:51
  • also i think the 3rd option is partially what i am looking for for creating a new file. However, what is currently the case is that i have to manually switch the boolean condition. what i meant was, i want the `bool someCondition = true;` to be set whenever a new session (e.g. everytime i click run program) occurs; ive done some research on that however all i am getting is cookie and HTTP sessions examples. i guess i dont know what it is referred to everytime we click "run", and a new console window pops up, isnt it session? – Cataster Nov 12 '20 at 06:56
  • yeah I missed it, calling Dispose twice writes records twice, so we need to track it too. I updated my answer. *new session (e.g. everytime i click run program)* this depends on your application type and how you can detect new sessions. you might want to post different question for that – Yehor Androsov Nov 12 '20 at 06:58
  • ok sounds good! although this is not critical now, i am thinking longterm if we have this job scheduled, it wouldnt make sense to append the same information every 10 minutes for example. or maybe instead of ceating a new file, somehow check if the information is already there (e.g. row already exists, so either overwrite it or dont append?) – Cataster Nov 12 '20 at 07:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/224454/discussion-between-yegor-androsov-and-cataster). – Yehor Androsov Nov 12 '20 at 07:02
  • with `writer = null;` i was able to use `writer?.Dispose();` – Cataster Nov 12 '20 at 07:34