Link to home
Create AccountLog in
C#

C#

--

Questions

--

Followers

Top Experts

Avatar of Mr_Fulano
Mr_Fulano🇺🇸

Need help with foreach (or any loop) statement when creating an XML document.

Hello, I am using C# in Visual Studio 2022. 


I am reading from a CSV file and producing an XML file from the data stored within the CSV. -- I can do that for the most part, but am having a problem with LOOPING through a series of similar rows.


Lets take an example.   Let say this is my code: 


XMLFile in the code below is the path to my new XML file.


private void button2_Click(object sender, EventArgs e)
        {
            using (StreamWriter sw = new StreamWriter(XMLFile))
            {
                XDocument Tree = new XDocument(
                new XElement("Root",
                new XElement("Element1",
                new XElement("Element2",
                new XElement("Element3","Category"),

                new XElement("People",
                new XElement("Name", ""),
                new XElement("Address", ""),
                new XElement("City", ""),
                new XElement("State", ""),
                new XElement("Zip", ""),
                new XElement("Email", "")

                )))));
                sw.WriteLine(Tree);
            }

Open in new window

The code above works and produces the following XML file below.

<Root>
  <Element1>
    <Element2>
      <Element3>Category</Element3>
      <People>
        <Name></Name>
        <Address></Address>
        <City></City>
        <State></State>
        <Zip></Zip>
        <Email></Email>
      </People>
    </Element2>
  </Element1>
</Root>

Open in new window

The problem is that I'm having a difficult time coding a foreach loop into my code, so as to loop through the list of "people" in the csv file.


Below is sample madeup data to use for this example.

Name,Address,City,State,Zip,Email
"Smith, Jane",123 Mian St.,Denver,CO,80299,Jane@someemail.com
"Moore, Gregory",2828 Sail Boat Ct.,Key Biscayne,FL,33120,Greg@someemail.com
"Jones, Kelly",124 South St.,Ashville,NC,28801,Kelly@janesemail.com
"Brown, Molly",389 Yellow Lane,Glenwood,NC,28816,Molly@somemail.com
"Woodburn, Jonathan",9087 Old Fort Rd.,Hartsville,TN,37074,JW@janesemail.com

Open in new window


What I would like to do is produce an XML structure for each of the people in the csv file, with their corresponding values.  That repeating structure would appear under <People> and each individual would be listed one after the other. 


Can someone please assist? 

Thank you.

Zero AI Policy

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of louisfrlouisfr

It depends. Do you have to create those specific elements (Name, Address...) or do you put in the XML whatever is in the header?
It depends also how you read the csv. If you get the data in a DataTable, or an IEnumerable<string[]>, or something else.
Here is some generic code with some missing code you have to adapt.
using (StreamWriter sw = new StreamWriter(XMLFile))
{
    var element2 = new XElement("Element2",
                        new XElement("Element3", "Category"));
    foreach(var line in WhateverCsvReader.Read(csvfile))
    {
        var people = new XElement("People");

        // call people.Add for each element you want to add from the values in 'line'

        element2.Add(people);
    }
    XDocument Tree = new XDocument(
                        new XElement("Root",
                            new XElement("Element1", element2)));
    sw.WriteLine(Tree);
}

Open in new window


Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Hello Louisfr,  here are answers to your questions:

Question #1), "Do you have to create those specific elements (Name, Address...) or do you put in the XML whatever is in the header?"

The elements outside the "People" element are already there and are built when the XML file is created.

The only elements that would get added are the ones inside the "People" element. None of those are created yet. Those would be added for each of the records contained in the CSV. In other words, I would ADD a Name, Address, City, State, Zip and Email for each person contained in the CSV file (i.e. for each record - considering each person to be a new record).

So, simply put... I have to add the Element Tag and the value under each of the headers for each record.

Question #2). "It depends also how you read the csv. If you get the data in a DataTable, or an IEnumerable<string[]>, or something else."

Yes, the "People" records in the CSV file ARE put into a DataGridView control, so that the enduser can view them before creating the XML file. I am not using the DataGridView as a Data Source once its populated, but rather as a visual reference for the enduser to inspect the data.  

Regarding how I read the records in....I'm not yet doing anything with the records within the "People" element, because I'm a bit stuck on how to process them right now. I'm still trying to figure out which way to go...

I will try your solution tonight after dinner. I'll get back to you soon.

Thank you very much for your help!


Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Hi Louisfr, I tried your code and it works to create the file and the elements within the XML, but when it gets to the "foreach" loop, I'm still having a problem.

For testing purposes, I'm adding the entire line for each record to make sure its looping correctly. I'll parse the line for the different values later.

So, here's the issue... using the code below:

foreach(var line in WhateverCsvReader.Read(csvfile))
    {
        var people = new XElement("People");

        people.Add(new XElement("Person", line));

        element2.Add(people);
    }

Open in new window


It will ADD the <Person> element and the Value found in the CSV file, to the <People> element, like it should, but it only adds one entry for the LAST person.  In other words, it keeps adding the same element Tag --  in essence, its replacing the element each time it loops, so when it writes to file, it writes out the last value. I think it knows there is a person Tag already there, and it keeps replacing it over and over.

Clearly, I'm missing something...

Any suggesitons? 

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


SOLUTION
Avatar of kaufmedkaufmed🇺🇸

Link to home
membership
Log in or create a free account to see answer.
Signing up is free and takes 30 seconds. No credit card required.
Create Account

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Hello Kaufmed, how have you been?  This is very nice... thank you. I'll retweet my code to make this work for me. Thank you...!!!

Avatar of kaufmedkaufmed🇺🇸

I am well, thank you for asking; I hope you are as well  =)

I'll also mention that you can use the built-in serializers such as XmlSerializer or DataContractSerializer if you like. They just tend to require a few extra attributes to decorate the classes--it's not too much extra, just something to be aware of.

There is no built-in CsvSerializer, to my knowledge.

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Thank you.... I appreciate your help!

Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Hi Louisfr, you solution does not work. It works to create the elements in their desired order, sure... but the point of the question was to loop through the cvs file and then add that as XML elements to the XML file. You code does not write the looped lines to the file. It's not writing on each loop, its write the last line at the end of the loop. So, no... your solution doesn't solve my problem. -- Thanks for trying.


ASKER CERTIFIED SOLUTION
Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Link to home
membership
Log in or create a free account to see answer.
Signing up is free and takes 30 seconds. No credit card required.

the point of the question was to loop through the cvs file and then add that as XML elements to the XML file
The code I wrote works according to your initial specs. It adds a People element for each line of the csv. There was no "Person" element in the sample xml you have shown. I think the problem is probably in what you replaced the commented part with.

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Louisfr, the code didn't work for me under testing.  I posted some problems above. Maybe look at them and you'll see what problems I was having.

Regarding the "Person" element is just on extra element I added in the end, but it has nothing to do with the looping process.

Thank you for your help.

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


Louisfr, the code didn't work for me under testing.  I posted some problems above. Maybe look at them and you'll see what problems I was having.

I looked at them.
My initial code writes this to the file:
<Root>
  <Element1>
    <Element2>
      <Element3>Category</Element3>
      <People>
        <Name>Smith, Jane</Name>
        <Address>123 Mian St.</Address>
        <City>Denver</City>
        <State>CO</State>
        <Zip>80299</Zip>
        <Email>Jane@someemail.com</Email>
      </People>
      <People>
        <Name>Moore, Gregory</Name>
        <Address>2828 Sail Boat Ct.</Address>
        <City>Key Biscayne</City>
        <State>FL</State>
        <Zip>33120</Zip>
        <Email>Greg@someemail.com</Email>
      </People>
      <People>
        <Name>Jones, Kelly</Name>
        <Address>124 South St.</Address>
        <City>Ashville</City>
        <State>NC</State>
        <Zip>28801</Zip>
        <Email>Kelly@janesemail.com</Email>
      </People>
      <People>
        <Name>Brown, Molly</Name>
        <Address>389 Yellow Lane</Address>
        <City>Glenwood</City>
        <State>NC</State>
        <Zip>28816</Zip>
        <Email>Molly@somemail.com</Email>
      </People>
      <People>
        <Name>Woodburn, Jonathan</Name>
        <Address>9087 Old Fort Rd.</Address>
        <City>Hartsville</City>
        <State>TN</State>
        <Zip>37074</Zip>
        <Email>JW@janesemail.com</Email>
      </People>
    </Element2>
  </Element1>
</Root>

Open in new window

If the problem is not with the Person element you added, I don't know what it is.

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Loouisfr, as I had mentioned, I tired your code verbatim (I cut and pasted it) and it DID NOT WORK... I don't know why you can't accept that. The person element is simply one more element.... nothing more. It does not affect the loop in any way.

OK, so as I said, you offered a suggestion for a solution and it did not work for me. Nonetheless, I thanked you for your help and moved on.  

At this point allow me to provide some friendly advice. Accept that you may have not provided the ultimate solution. One that worked for the question asker. Accept that, be courteous and move on. Don't harp or dwell on trying to proove how good your solution was, because it did not work for me... simple as that.

Thank you for trying, but I found a solution that worked for me - and the Person element has NOTHING to do with the loop and you know that. 

I'm not trying to prove anything. I'm trying to understand why it didn't work for you.
It is a normal reaction when someone describes what's happening and the code is so straight-forward that anybody can see it can't possibly be doing that.

If you're satisfied with your solution, good for you.
I won't bother you anymore.

Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Louisfr, don't get diffusive. You're not bothering me. I just keep saying over and over that I copied your code (cut and pasted it) and it didn't work for me. I sent you a few different messages about what I was experiencing and I didn't hear back. So, I had to find a different solution.

You're not a bother and I DO appreciate your help, but I keep saying ... when I used your code, it did loop, but it did not write the loop to file. I think part of the problem is that you write the XML file to disk at the end, I do it upfront and then append to it as I move along. So, I'm not saying your code is useless... I'm saying it did not work for my situation. That's all.

Again, I am grateful for your help and do look forward to your help the next time around.  

Thank you again!  

I sent you a few different messages about what I was experiencing and I didn't hear back.
Indeed, sometimes my real work prevents me from answering here for several days. Sorry if the delay made you think I didn't want to reply.
you write the XML file to disk at the end, I do it upfront and then append to it as I move along
No you don't. Not in the code you marked as a solution. You do only one WriteLine, after adding everything to your customers variable. As I did in my code. And that's ok.
If you want to write it element after element, you can use an XmlWriter.
I copied your code (cut and pasted it) and it didn't work for me
The code I posted cannot work as is. It uses a WhateverCsvReader. That is not a class or a variable defined anywhere in that code. You necessarily modified the code.

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Louisfr, I know about the "WhateverCSVReader" part. I modified that.  I also understand about real work, obligations and how busy we all get. 

However, since you are SO adamant about your code working correctly, lets do what you should have done to begin with... lets have you post your COMPLETE solution to my question. Lets see how you made your code work from top to bottom, because to your own admission, your code was incomplete and as you stated "The code I posted cannot work as is..."  So, lets see how you actually made your code produce the output I had requested in my question. -- That's the part you have yet to show!  You've spent a huge amount of time dwelling on this, but you are reluctant to show your code... DO IT. Stop saying it works and show us all how you made this work, which if you had done that from the beginning we wouldn't be having this conversation... so, let see it. Top to bottom, don't leave anything out. I have the data set, so I can plug that in on my side.  



Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


you are reluctant to show your code
No, I'm not. The two parts I left out are the  reading of the csv, because I didn't know how you read it, and how to include the data in the xml tree, because it depends on the reader you used and on how you identify which data to put where.

In my initial code, I used a version of the CsvReader I posted on another question of yours. And for what to do with the data, I had two versions, one using a known fixed format for the csv lines, and one which uses the header of the csv.

First, the CsvReader:
static class CsvReader
{
    static Regex splitRegex = new(@$"(?!$)\G(?:""(?<quoted>(?:[^""]|"""")*)""|(?<unquoted>[^,]*))(?:,|$)", RegexOptions.Compiled);
    public static IEnumerable<string[]> Read(string path)
    {
        foreach (var line in File.ReadAllLines(path))
        {
            yield return splitRegex.Matches(line)
                                   .Select(m => m.Groups["quoted"].Success
                                                ? m.Groups["quoted"].Value.Replace("\"\"", "\"")
                                                : m.Groups["unquoted"].Value)
                                   .ToArray();
        }
    }

    public static IEnumerable<Dictionary<string, string>> ReadWithHeader(string path)
    {
        Dictionary<string, int>? header = null;
        foreach (var line in File.ReadAllLines(path))
        {
            string[] values = splitRegex.Matches(line)
                                        .Select(m => m.Groups["quoted"].Success
                                                     ? m.Groups["quoted"].Value.Replace("\"\"", "\"")
                                                     : m.Groups["unquoted"].Value)
                                        .ToArray();
            if (header == null)
            {
                header = new Dictionary<string, int>(values.Select((h, i) => new KeyValuePair<string, int>(h, i)));
            }
            else
            {
                yield return header.ToDictionary(kv => kv.Key, kv => values[kv.Value]);
            }
        }
    }
}

Open in new window

The first version of the code:
const string XMLFile = "people.xml";
using (var sw = new StreamWriter(XMLFile))
{
    var element2 = new XElement("Element2",
                                    new XElement("Element3", "Category"));
    foreach (var line in CsvReader.Read("people.csv"))
    {
        var people = new XElement("People");
        people.Add(new XElement("Name", line[0]),
                   new XElement("Address", line[1]),
                   new XElement("City", line[2]),
                   new XElement("State", line[3]),
                   new XElement("Zip", line[4]),
                   new XElement("Email", line[5]));
        element2.Add(people);
    }
    var Tree = new XDocument(new XElement("Root",
                                new XElement("Element1", element2)));
    sw.WriteLine(Tree);
}

Open in new window

Second version:
const string XMLFile = "people.xml";
using (var sw = new StreamWriter(XMLFile))
{
    var element2 = new XElement("Element2",
                                    new XElement("Element3", "Category"));
    foreach (var line in CsvReader.ReadWithHeader("people.csv"))
    {
        var people = new XElement("People");
        foreach (var cell in line)
        {
            people.Add(new XElement(cell.Key, cell.Value));
        }
        element2.Add(people);
    }
    var Tree = new XDocument(new XElement("Root",
                                new XElement("Element1", element2)));
    sw.WriteLine(Tree);
}

Open in new window

I have since added the "Person" element.

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Louisfr, WOW... had you posted the entire code when you made your suggestion... one, we wouldn't be having this discussion now and two, it would have saved me A LOT of time trying to figure out how to use your suggestions.

So, I guess the lesson learned here is - if you don't have the time to share all your code or answer questions about your suggestions, maybe you should reconsider your contributions here. You said it yourself, you got busy, which is understandable, but maybe you need to rethink contributing if you really don't have the time. 

I think this is a mute point now....

So, I guess the lesson learned here is - if you don't have the time to share all your code or answer questions about your suggestions, maybe you should reconsider your contributions here.
Maybe the lesson is, if you are in such a hurry that me not reacting for just 1 day is critical, then you should have told so in the first place.
I posted my incomplete code with questions on March 15th at 18:15 in my timezone. I couldn't possibly know what you used to read the csv nor know if the xml data was fixed or based on the csv header.
You tried to answer those two questions 30 minutes later.
I was having dinner at the time, and then I used my time as any normal person do at night by not coming on this site.
By the time I woke up the next day, you had already started talking nonsense about that code not working, without showing the code you actually tried (and you are still reluctant to reveal what it was).
I replied the day after that. That's one day I didn't log on this site.
If there is something that should be reconsidered, it's your attitude.

Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

"...started talking nonsense about that code not working"...

OK, so this stops here...I've been really patient with you thus far. In the 15 years I've been on EE, I've always been considerate, treated people with courtesy and been grateful with the contributors that try to assist me. I know life takes its toll on all of us and we all have busy lives. However, you're like a guy I used to work with about a decade ago... he was never wrong - no matter how wrong he was... in his mind, he was never wrong.

In life if you're going to do something do it right... everything else is an excuse. It doesn't matter how much you had to do, once you commit to helping people on this site you're committed. If your solution doesn't work for them, be there to help. You get emails and I'm sure you got emails saying I had posted a comment... what did you think I was posting you about....?  You posted IMCOMPLETE code. That's why it DID NOT WORK. I tried your code in my project and it failed. If you're going to post incomplete solutions and then not answer questions about your code, you're not doing anyone any favors. Go have dinner and enjoy your time, but don't commit to helping others if you don't have the time or want to log in.

Having seen your code after you posted it proves to me, that I would not have used it had I known I had to do so much programming gymnastics to do what I was able to do essentially in 10 lines of code.

Your code DID NOT WORK for me..... its not nonsense its a FACT live with it...!!!

Having said all that, I am big enough and mature enough to still say... thank you for trying to help. Good luck and stay well. 




I was going to make a long point-by-point reply but I am spending too much time already on this.
Nobody is paying me for that, eating and sleeping comes before helping people on this site. And my paying job too.
Thinking otherwise is ridiculous.
I don't log in on week-ends either.
Having seen your code after you posted it proves to me, that I would not have used it had I known I had to do so much programming gymnastics to do what I was able to do essentially in 10 lines of code.
I could have written much simpler code, but I thought you wanted to read a CSV file, and have a result looking like this:
<Person>
  <Name>Smith, Jane</Name>
  <Address>123 Mian St.</Address>
  <City>Denver</City>
  <State>CO</State>
  <Zip>80299</Zip>
  <Email>Jane@someemail.com</Email>
</Person>

Open in new window

instead of what your 10 lines of code produces:
<Person>
  <Name>"Smith</Name>
  <Address> Jane"</Address>
  <City>123 Mian St.</City>
  <State>Denver</State>
  <Zip>CO</Zip>
  <Email>80299</Email>
</Person>

Open in new window


Your code DID NOT WORK for me..... its not nonsense its a FACT live with it...!!!
What I call nonsense is this: "it keeps adding the same element Tag --  in essence, its replacing the element each time it loops, so when it writes to file, it writes out the last value"
If it's not nonsense, then why are you so reluctant to post the code you actually tried?

Avatar of Mr_FulanoMr_Fulano🇺🇸

ASKER

Louisfr, I'm not reluctant. I don't have that code anymore. I delete it, because it wasn't working. Otherwise I would post it.  Listen, I've been really gracious with you on other occasions. I've given you great reviews, because your code helped me. -- Ask yourself this... why in the world would I say your code didn't work if it did. I would have simply used it, given you a great review (as I have in the past) thank you (as I always have in the past) and moved on. Saying my explanation is nonsense is simply sheer arrogance... why would I make that up?

The code, you provided (which I cut and pasted and changed the CSV read part) did not work. Why in heavens name can you simply not understand that is beyond me.

That said, you and I can agree on one thing... this has gone on way too long. I really urge you to move on... have some dinner and sleep it off. I'm not replying to anymore of these posts I suggest you do the same.

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.

C#

C#

--

Questions

--

Followers

Top Experts

C# is an object-oriented programming language created in conjunction with Microsoft’s .NET framework. Compilation is usually done into the Microsoft Intermediate Language (MSIL), which is then JIT-compiled to native code (and cached) during execution in the Common Language Runtime (CLR).