Converter o ficheiro CSV para XML
Tenho isto até agora, usando LINQ:
String[] File = File.ReadAllLines(@"C:\text.csv");
String xml = "";
XElement top = new XElement("TopElement",
from items in File
let fields = items.Split(';')
select new XElement("Item",
new XElement("Column1", fields[0]),
new XElement("Column2", fields[1]),
new XElement("Column3", fields[2]),
new XElement("Column4", fields[3]),
new XElement("Column5", fields[4])
)
);
File.WriteAllText(@"C:\xmlout.xml", xml + top.ToString());
Isto é para uma quantidade fixa de colunas, mas minha .CSV tem um número diferente de colunas em cada linha.
Como você encaixaria algum tipo de laço nisto, dependendo de quantas palavras (colunas) existem em cada linha do .CSV?
Thnx
5 answers
var lines = File.ReadAllLines(@"C:\text.csv");
var xml = new XElement("TopElement",
lines.Select(line => new XElement("Item",
line.Split(';')
.Select((column, index) => new XElement("Column" + index, column)))));
xml.Save(@"C:\xmlout.xml");
Entrada:
A;B;C
D;E;F
G;H
Resultado:
<TopElement>
<Item>
<Column0>A</Column0>
<Column1>B</Column1>
<Column2>C</Column2>
</Item>
<Item>
<Column0>D</Column0>
<Column1>E</Column1>
<Column2>F</Column2>
</Item>
<Item>
<Column0>G</Column0>
<Column1>H</Column1>
</Item>
</TopElement>
No caso de querer usar os cabeçalhos como nomes dos elementos:
var lines = File.ReadAllLines(@"C:\text.csv");
string[] headers = lines[0].Split(',').Select(x => x.Trim('\"')).ToArray();
var xml = new XElement("TopElement",
lines.Where((line, index) => index > 0).Select(line => new XElement("Item",
line.Split(',').Select((column, index) => new XElement(headers[index], column)))));
xml.Save(@"C:\xmlout.xml");
Ensaio Unitário:
[TestMethod]
public void convert_csv_to_xml()
{
// Setup
var csvPath = @"Testware\vendor.csv";
var xmlPath = @"Testware\vendor.xml";
// Test
var success = DocumentConverter.Instance.CsvToXml(csvPath, xmlPath);
// Verify
var expected = File.Exists(xmlPath) && success;
Assert.AreEqual(true, expected);
}
CSV para XML:
public class DocumentConverter
{
#region Singleton
static DocumentConverter _documentConverter = null;
private DocumentConverter() { }
public static DocumentConverter Instance
{
get
{
if (_documentConverter == null)
{
_documentConverter = new DocumentConverter();
}
return _documentConverter;
}
}
#endregion
public bool CsvToXml(string sourcePath, string destinationPath)
{
var success = false;
var fileExists = File.Exists(sourcePath);
if (!fileExists)
{
return success;
}
var formatedLines = LoadCsv(sourcePath);
var headers = formatedLines[0].Split(',').Select(x => x.Trim('\"').Replace(" ", string.Empty)).ToArray();
var xml = new XElement("VendorParts",
formatedLines.Where((line, index) => index > 0).
Select(line => new XElement("Part",
line.Split(',').Select((field, index) => new XElement(headers[index], field)))));
try
{
xml.Save(destinationPath);
success = true;
}
catch (Exception ex)
{
success = false;
var baseException = ex.GetBaseException();
Debug.Write(baseException.Message);
}
return success;
}
private List<string> LoadCsv(string sourcePath)
{
var lines = File.ReadAllLines(sourcePath).ToList();
var formatedLines = new List<string>();
foreach (var line in lines)
{
var formatedLine = line.TrimEnd(',');
formatedLines.Add(formatedLine);
}
return formatedLines;
}
}
Nota:
Extendi a solução do Vlax removendo uma vírgula para cada uma das entradas da linha CSV que causou uma excepção de tempo de execução com base num índice que está fora dos limites em relação ao cabeçalho da coluna.
Cinchoo ETL - uma biblioteca de código aberto disponível para fazer a conversão do CSV para Xml facilmente com poucas linhas de código
Para uma amostra CSV:
string csv = @"Id, Name, City
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
";
StringBuilder sb = new StringBuilder();
using (var p = ChoCSVReader.LoadText(csv)
.WithFirstLineHeader()
)
{
using (var w = new ChoXmlWriter(sb)
.Configure(c => c.RootName = "Employees")
.Configure(c => c.NodeName = "Employee")
)
w.Write(p);
}
Console.WriteLine(sb.ToString());
Saída Xml:
<Employees>
<Employee>
<Id>1</Id>
<Name>Tom</Name>
<City>NY</City>
</Employee>
<Employee>
<Id>2</Id>
<Name>Mark</Name>
<City>NJ</City>
</Employee>
<Employee>
<Id>3</Id>
<Name>Lou</Name>
<City>FL</City>
</Employee>
<Employee>
<Id>4</Id>
<Name>Smith</Name>
<City>PA</City>
</Employee>
<Employee>
<Id>5</Id>
<Name>Raj</Name>
<City>DC</City>
</Employee>
</Employees>
Verifique o artigo do CodeProject para obter mais ajuda.
Disclaimer: eu sou o autor desta biblioteca.
Aqui fornece uma solução sem usar LINQ aninhado, mais simples de entender.
- usa o Linq para Xml.
- suporta diferentes delimitadores (const)
- suporta cada linha tem diferentes fatias
O conteúdo de input.csv
:
A,B,C
D,E,F
G,H
O código do processo:
Program.cs
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace CSVtoXML
{
class Program
{
private static void AddContentForEachLine(string line, ref XElement xmlTree)
{
var currentTree = new XElement("Item");
const string delimiter = ","; // Can be changed based on the actual situation
string[] slices = line.Split(delimiter);
for (int i = 0; i < slices.Count(); i++)
currentTree.Add(new XElement($"Column{i}", slices[i].ToString()));
xmlTree.Add(currentTree);
}
static void Main(string[] args)
{
var basePath = Environment.CurrentDirectory;
var lines = File.ReadAllLines(Path.Combine(basePath, "../../..", @"input.csv"));
var xmlTree = new XElement("TopElement");
foreach (var line in lines)
{
AddContentForEachLine(line, ref xmlTree);
}
xmlTree.Save(Path.Combine(basePath, "../../..", @"output.xml"));
}
}
}
Depois de executar o código, o resultado é abaixo:
<?xml version="1.0" encoding="utf-8"?>
<TopElement>
<Item>
<Column0>A</Column0>
<Column1>B</Column1>
<Column2>C</Column2>
</Item>
<Item>
<Column0>D</Column0>
<Column1>E</Column1>
<Column2>F</Column2>
</Item>
<Item>
<Column0>G</Column0>
<Column1>H</Column1>
</Item>
</TopElement>
A solução visual studio completa para este código pode ser vista aqui:
https://github.com/yanglr/dotnetInterview/tree/master/CSVtoXML.