I'm trying to convert a csv to a xml.
CSV sample
ID;name;addresses;street;streetNR
1;peter;;streetTest;58784
1;peter;;street2;04512
this should look like
<ID>
<name>Peter</name>
<addresses>
<address>
<street>streetTest</street>
<plz>58784</plz>
</address>
<address>
<street>street2</street>
<plz>04512</plz>
</address>
</address>
</ID>
The file XML is huge. It's around 100 tags. What is the smartest way to do this? I already checked the forum but can't find something that fits well enough.
You can use XElement and Linq to convert the csv with header to xml
var header = File.ReadLines(inputfile).First().Split(';'); //Read header
var lines = File.ReadAllLines(inputfile).Skip(1); //Skip header row
//format the xml how you want
var xml = new XElement("EmpDetails",
lines.Select(line => new XElement("Item",
line.Split(';').Select((column, index) => new XElement(header[index], column)))));
xml.Save(outputfile); //output xml file
Try following xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.txt";
const string OUTPUT_FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Parse("<root></root>");
XElement root = doc.Root;
StreamReader reader = new StreamReader(INPUT_FILENAME);
int rowCount = 0;
string[] headers = null;
string line = "";
List<KeyValuePair<string, string[]>> data = new List<KeyValuePair<string, string[]>>(); ;
while ((line = reader.ReadLine()) != null)
{
if (++rowCount == 1)
{
headers = line.Split(new char[] { ';' }).ToArray();
}
else
{
string[] splitArray = line.Split(new char[] { ';' }).ToArray();
data.Add(new KeyValuePair<string,string[]>(splitArray[0], splitArray));
}
}
var groups = data.GroupBy(x => x.Key).ToList();
foreach (var group in groups)
{
int groupCount = 0;
XElement addresses = null;
foreach (var row in group)
{
string[] colData = row.Value;
if (++groupCount == 1)
{
XElement xGroup = new XElement("ID", new object[] {
new XElement("name", colData[1]),
new XElement("addresses")
});
root.Add(xGroup);
addresses = xGroup.Element("addresses");
}
int min = Math.Min(headers.Length, colData.Length);
XElement address = new XElement("address");
addresses.Add(address);
for (int i = 3; i < min; i++)
{
XElement xCol = new XElement(headers[i], colData[i]);
address.Add(xCol);
}
}
}
doc.Save(OUTPUT_FILENAME);
}
}
public class Material
{
public string name { get; set; }
public string className { get; set; }
public List<Property> properties { get; set; }
}
public class Property
{
public string property { get; set; }
public string format { get; set; }
public string value { get; set; }
}
}
Related
I'm sorry that I interrupt you in this manner, I'm new to C# and I've been struggling with this problem for days... Maybe it will seem easy for you :)
I have this text file in this format
name|ID|domain|grade|verdict
Ryan|502322|Computers|9,33|Undefined
Marcel|302112|Automatics|6,22|Undefined
Alex|301234|Computers|5,66|Undefined
Leo|201122|Automatics|3,22|Undefined
How can I sort the text file using any methods (including LINQ) so that the list from the text file will be ordered by domain, and then descending by the grade column? Like this:
name|ID|domain|grade|verdict
Marcel|302112|Automatics|6,22|Undefined
Leo|201122|Automatics|3,22|Undefined
Ryan|502322|Computers|9,33|Undefined
Alex|301234|Computers|5,66|Undefined
To read the file, I'm using var Students = File.ReadAllLines(#"filepath");, I don't know if it's the smartest approach, and then I write using File.WriteAllLines
Thanks in advance! Sorry once again, I know it should be easy, but for me is really tuff :(
You can use some thing like this:
var students= File.ReadAllLines(#"filepath");
var headers = lines[0];
students = lines.Skip(1).ToArray();
var orders = lines.Select(x => x.Split('|'))
.Select(x => new { Domain = x[2], Grade = int.Parse(x[3].Replace(",", "")), All = x })
.OrderBy(x => x.Domain).ThenByDescending(x => x.Grade).Select(x => string.Join("|", x.All)).ToList();
orders.Insert(0, headers);
students=orders.ToArray();
try following code:
private void ReadFile()
{
char Delimiter = '|';
string[] Lines = File.ReadAllLines(#"E:\RaftehHa.txt", Encoding.Default);
List<string[]> FileRows = Lines.Select(line =>
line.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries)).ToList();
DataTable dt = new DataTable();
dt.Columns.AddRange(FileRows[0].Select(col => new DataColumn() { ColumnName = col }).ToArray());
FileRows.RemoveAt(0);
FileRows.ForEach(row => dt.Rows.Add(row));
DataView dv = dt.DefaultView;
dv.Sort = " ID ASC ";
dt = dv.ToTable();
dataGridView1.DataSource = dt;
}
A bit the same as already mentioned above, but as you mention you are new to C#, I have tried to add a little bit of structure to the code, but leaving the completion to you.
public class Data
{
public Data(string inputLine)
{
var split = inputLine.Split('|');
Name = split[0];
Id = int.Parse(split[1]);
Domain = split[2];
Grade = double.Parse(split[3].Replace(",", "."));
Verdict = split[4];
}
public string Name { get; }
public int Id { get; }
public string Domain { get; }
public double Grade { get; }
public string Verdict { get; }
}
public class DataFile
{
public static IEnumerable Read(string fileName)
{
var input = File.ReadAllLines(fileName);
return input.Skip(1).Select(p => new Data(p)); // skip header
}
public static void Write(IEnumerable data)
{
// todo :)
}
}
void Main()
{
var input = DataFile.Read(#"C:\Temp\ExampleData.txt");
var result = input.OrderBy(p => p.Domain).ThenByDescending(p => p.Grade);
DataFile.Write(result);
}
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
enum DomainType {
Automatics, // 0
Computers // 1
}
class Data {
public int Id { get; set; }
public string Name { get; set; }
public string Verdict { get; set; }
public DomainType Domain { get; set; }
public Tuple<int, int> Grade { get; set; }
}
public static class Program {
static IEnumerable<Data> FileContent(string path) {
string line;
using (var reader = File.OpenText(path))
{
bool skipHeader = false;
while((line = reader.ReadLine()) != null)
{
if (!skipHeader) {
skipHeader = true;
continue;
}
var fields = line.Split('|');
string name = fields[0];
int id = int.Parse(fields[1]);
var domain = (DomainType)Enum.Parse(typeof(DomainType), fields[2]);
var grade = Tuple.Create(int.Parse(fields[3].Split(',')[0]),
int.Parse(fields[3].Split(',')[1]));
string verdict = fields[4];
var data = new Data() {
Name = name, Id = id, Domain = domain, Grade = grade, Verdict = verdict };
yield return data;
}
}
}
public static void Main() {
var result = FileContent("path_to_file").OrderBy(data => data.Domain);
foreach (var line in result) {
Console.WriteLine(line.Name);
}
}
}
I have ini files that contains data from server.
Map.ini
[MAP_1]
MapType = 1
MapWar = 1
Position = 42.03,738.2,737.3
[MAP_2]
MapType = 1
MapWar = 1
Position = 42.03,738.2,737.3
How to read map.ini that contains this kind of files and save it in Dictionary or List.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
List<Map> maps = new List<Map>();
Map map = null;
StreamReader reader = new StreamReader(FILENAME);
string line = "";
while ((line = reader.ReadLine()) != null)
{
line.Trim();
if (line.Length > 0)
{
if(line.StartsWith("["))
{
string name = line.Trim(new char[] { '[', ']' });
map = new Map();
maps.Add(map);
map.name = name;
}
else
{
string[] data = line.Split(new char[] { '=' });
switch (data[0].Trim())
{
case "MapType" :
map.mapType = int.Parse(data[1]);
break;
case "MapWar":
map.mapWar = int.Parse(data[1]);
break;
case "Position":
string[] numbers = data[1].Split(new char[] { ',' });
map.x = decimal.Parse(numbers[0]);
map.y = decimal.Parse(numbers[1]);
map.z = decimal.Parse(numbers[2]);
break;
default :
break;
}
}
}
}
Dictionary<string, Map> dict = maps
.GroupBy(x => x.name, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
public class Map
{
public string name { get; set; }
public int mapType { get; set; }
public int mapWar { get; set; }
public decimal x { get; set; }
public decimal y { get; set; }
public decimal z { get; set; }
}
}
Say we want to calculate how the price of some fruits are changing. Starting from a CSV file:
Day,Name,Kind,Price
2019-09-04,"apple","red delicious",63.09
2019-09-04,"apple","ginger crisp",52.14
2019-09-04,"orange","navel",41.18
2019-09-03,"apple","red delicious",63.07
2019-09-03,"apple","ginger crisp",52.11
2019-09-03,"orange","navel",41.13
2019-09-02,"apple","red delicious",63.00
2019-09-02,"apple","ginger crisp",52.00
2019-09-02,"orange","navel",41.00
with an unknown number of fruits and varieties, we can read the dataframe and build an extra column to use for matching.
var fruits_file = Path.Combine(root, "fruits.csv");
Deedle.Frame<int, string> df = Frame.ReadCsv(fruits_file);
Series<int, string> name = df.GetColumn<string>("Name");
Series<int, string> kind = df.GetColumn<string>("Kind");
var namekind = name.ZipInner(kind).Select(t => t.Value.Item1 + t.Value.Item2);
df.AddColumn("NameKind", namekind);
but the problem remains. Deedle.Series.Window() and Deedle.Series.Pairwise() make it possible to perform first-order differences, but not matching based on some string (namekind).
What is the right way to copy over a column LastPrice and subsequently calculate the Change?
Day,Name,Kind,Price,LastPrice,Change
2019-09-04,"apple","red delicious",63.09,63.07,0.02
2019-09-04,"apple","ginger crisp",52.14,52.11,0.03
2019-09-04,"orange","navel",41.18,41.13,0.05
2019-09-03,"apple","red delicious",63.07,63.00,0.07
2019-09-03,"apple","ginger crisp",52.11,52.00,0.11
2019-09-03,"orange","navel",41.13,41.00,0.13
2019-09-02,"apple","red delicious",63.00,,
2019-09-02,"apple","ginger crisp",52.00,,
2019-09-02,"orange","navel",41.00,,
See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace ConsoleApplication137
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Fruit fruit = new Fruit(FILENAME);
Fruit.PrintFruits();
Console.ReadLine();
}
}
public class Fruit
{
public static List<Fruit> fruits { get; set; }
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit() { }
public Fruit(string filename)
{
int count = 0;
StreamReader reader = new StreamReader(filename);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
Fruit newFruit = new Fruit();
if (fruits == null) fruits = new List<Fruit>();
fruits.Add(newFruit);
newFruit.day = DateTime.Parse(splitLine[0]);
newFruit.name = splitLine[1];
newFruit.kind = splitLine[2];
newFruit.price = decimal.Parse(splitLine[3]);
}
}
reader.Close();
}
public static void PrintFruits()
{
var groups = fruits.OrderBy(x => x.day)
.GroupBy(x => new { name = x.name, kind = x.kind })
.ToList();
foreach (var group in groups)
{
for (int i = 0; i < group.Count() - 1; i++)
{
Console.WriteLine("Old Date : '{0}', New Date : '{1}', Name : '{2}', Kind : '{3}', Old Price '{4}', New Price '{5}', Delta Price '{6}'",
group.ToList()[i].day.ToString("yyyy-MM-dd"),
group.ToList()[i + 1].day.ToString("yyyy-MM-dd"),
group.ToList()[i].name,
group.ToList()[i].kind,
group.ToList()[i].price.ToString(),
group.ToList()[i + 1].price.ToString(),
(group.ToList()[i + 1].price - group.ToList()[i].price).ToString()
);
}
}
}
}
}
I have code which loops through XML elements within multiple documents, and checks for a matching values within an array (cpltfids). If it finds a match for one of the array values in any of the XML elements, it creates a hard link for the file found in the XML.
The loop works fine for creating all of the hard links, but I am struggling to add code to do two more things:
If none of the array values are found in any of the XMLs, stop and do nothing.
Return the value of all array values that are never found at the end of the loop, so I can write them into a Message Box / Write Line at the end.
foreach (var assetC in assetElements)
{
var innerElementsC = assetC.Descendants(assetns + "Id").FirstOrDefault().Value;
if (!Array.Exists(cpltfids, element => element.Value == (innerElementsC)))
continue;
var assetname = assetC.Descendants(assetns + "Path").FirstOrDefault().Value;
var assetpath = Directory.GetFiles(assetmapdir, (assetname))[0].ToString();
CreateHardLink(Path.Combine(textBox3.Text, (assetname)), (assetpath), IntPtr.Zero);
}
UPDATE
Thanks for your comments. I am a total newb.
Summary of what I'm trying to do:
User loads x number of "CPL xml documents into a listbox.
Search x number of directories up (i will add more iterations later) from the xml(s) loaded and look for x number of files called ASSETMAP.xml
Parse all ASSETMAP.xml files and see if there are uuid matches with uuids in the user loaded xml.
For all matches found, construct the path to the files those uuids belong to, and make a hard link for each one in user specified dir.
The foreach loop lives inside a method i'm calling elsewhere in the program. Here is more context:
Method:
public bool UpdateNameInAssetmap(XDocument doc, string uuid, string newName)
{
var ns = doc.Root.GetDefaultNamespace();
var assetElements = doc.Descendants(ns + "Asset");
var result = false;
foreach (var assetC in assetElements)
{
var innerElementsC = assetC.Descendants(ns + "Id").First(); //nav to uuid id element in assetmap
if (!innerElementsC.Value.Equals(uuid))
continue;
var chunks = assetC.Elements(ns + "ChunkList").First().Elements(); //nav to chunk list element
foreach (var chunk in chunks)
{
chunk.Elements(ns + "Path").First().SetValue(newName); //update path element to new filename in assetmap
}
result = true;
}
return result;
}
Where I'm calling the method:
In this example I'm saying look 3 directories up from the user selected XML for the ASSETMAP.xml:
private void button5_Click(object sender, EventArgs e)
{
try
{
if (checkBox4.Checked)
{
var myList = listBox1.Items.Cast<String>().ToList();
foreach (var listitem in myList)
{
string folder = Path.GetFullPath(Path.Combine(listitem, "..\\..\\..\\"));
FindChildren(listitem, folder);
}
}
}
catch (Exception)
{
throw;
}
}
Condensed XML that I'm parsing to create the array of uuid's to look for:
<?xml version="1.0" encoding="UTF-8"?>
<CompositionPlaylist xmlns="http://www.smpte-ra.org/schemas/2067-3/2016" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Id>urn:uuid:f3412d2a-b7c6-4b88-8298-4b1287eb6b7f</Id>
<Annotation>test</Annotation>
<IssueDate>2018-03-14T18:04:50-00:00</IssueDate>
<SegmentList>
<Segment>
<Id>urn:uuid:2213e126-8943-44f2-b648-2ee1966bcae6</Id>
<SequenceList>
<cc:MainImageSequence xmlns:cc="http://www.smpte-ra.org/schemas/2067-2/2016">
<Id>urn:uuid:f24457fa-f9f0-4f98-823d-37b315076846</Id>
<TrackId>urn:uuid:4fe07410-9a36-41e8-90dc-6d948b6c9965</TrackId>
<ResourceList>
<Resource xsi:type="TrackFileResourceType">
<Id>urn:uuid:e6dda0de-d3b3-4132-b243-c5a32f906071</Id>
<Annotation>VIDEO_e6dda0de-d3b3-4132-b243-c5a32f906071.mxf</Annotation>
<EditRate>24000 1001</EditRate>
<IntrinsicDuration>120</IntrinsicDuration>
<EntryPoint>0</EntryPoint>
<SourceDuration>120</SourceDuration>
<SourceEncoding>urn:uuid:b54c9259-db31-4c26-a2f1-1fb2d9289a91</SourceEncoding>
<TrackFileId>urn:uuid:e6dda0de-d3b3-4132-b243-c5a32f906071</TrackFileId>
<Hash>6dj4cRq4fjj0hFHiCkzelQaBsmI=</Hash>
</Resource>
</ResourceList>
</cc:MainImageSequence>
<cc:MainAudioSequence xmlns:cc="http://www.smpte-ra.org/schemas/2067-2/2016">
<Id>urn:uuid:0482c0a9-a9bf-4eef-8242-827f5fc0ed04</Id>
<TrackId>urn:uuid:8ee689f1-d8d2-47d0-9aab-0649bc8dd107</TrackId>
<ResourceList>
<Resource xsi:type="TrackFileResourceType">
<Id>urn:uuid:d669a9dc-bb65-4e6c-8ca5-8c4c7db78366</Id>
<Annotation>AUDIO_d669a9dc-bb65-4e6c-8ca5-8c4c7db78366.mxf</Annotation>
<EditRate>48000 1</EditRate>
<IntrinsicDuration>240240</IntrinsicDuration>
<EntryPoint>0</EntryPoint>
<SourceDuration>240240</SourceDuration>
<SourceEncoding>urn:uuid:12c5eb01-05ec-4a52-a849-a93a75a81abe</SourceEncoding>
<TrackFileId>urn:uuid:d669a9dc-bb65-4e6c-8ca5-8c4c7db78366</TrackFileId>
<Hash>LqYEfjfJ4Ut868Ev7dEPAOZ2pQU=</Hash>
</Resource>
</ResourceList>
</cc:MainAudioSequence>
<cc:MainAudioSequence xmlns:cc="http://www.smpte-ra.org/schemas/2067-2/2016">
<Id>urn:uuid:a982cff3-45b6-4c13-9983-10a1c4c7cba6</Id>
<TrackId>urn:uuid:650715f4-fd76-49b8-af67-86064d68e883</TrackId>
<ResourceList>
<Resource xsi:type="TrackFileResourceType">
<Id>urn:uuid:56b010e4-f3e1-46f6-87e9-80b6ea099651</Id>
<Annotation>AUDIO_56b010e4-f3e1-46f6-87e9-80b6ea099651.mxf</Annotation>
<EditRate>48000 1</EditRate>
<IntrinsicDuration>240240</IntrinsicDuration>
<EntryPoint>0</EntryPoint>
<SourceDuration>240240</SourceDuration>
<SourceEncoding>urn:uuid:2ddc6740-96ab-4a68-b9fb-57db49642089</SourceEncoding>
<TrackFileId>urn:uuid:56b010e4-f3e1-46f6-87e9-80b6ea099651</TrackFileId>
<Hash>4u6ErtEdI9pXNIMDrQ/EB/aKuw8=</Hash>
</Resource>
</ResourceList>
</cc:MainAudioSequence>
<cc:SubtitlesSequence xmlns:cc="http://www.smpte-ra.org/schemas/2067-2/2016">
<Id>urn:uuid:d4825bd1-a294-41f7-820c-2b7658e06415</Id>
<TrackId>urn:uuid:31535e83-89ee-4e29-a8f4-d9a36eaff215</TrackId>
<ResourceList>
<Resource xsi:type="TrackFileResourceType">
<Id>urn:uuid:fbb9e1cc-6f2e-43b3-a01c-a3b34d04252a</Id>
<Annotation>SUB_fbb9e1cc-6f2e-43b3-a01c-a3b34d04252a.mxf</Annotation>
<EditRate>24000 1001</EditRate>
<IntrinsicDuration>120</IntrinsicDuration>
<EntryPoint>0</EntryPoint>
<SourceDuration>120</SourceDuration>
<SourceEncoding>urn:uuid:8562a7a6-43a4-4e30-bf6b-7b541ccfa900</SourceEncoding>
<TrackFileId>urn:uuid:fbb9e1cc-6f2e-43b3-a01c-a3b34d04252a</TrackFileId>
<Hash>eJVhYF6TuOo/vu7KE6qsmFqUnUk=</Hash>
</Resource>
</ResourceList>
</cc:SubtitlesSequence>
</SequenceList>
</Segment>
`
Sample XML that I'm searching for (ASSETMAP.xml). There may be more than one, hence the foreach loop.
<?xml version="1.0" encoding="UTF-8"?>
<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM">
<Id>urn:uuid:94958496-dde9-49b0-ad50-1dda6d4bba67</Id>
<Creator>test</Creator>
<VolumeCount>1</VolumeCount>
<IssueDate>2018-03-14T17:59:32-00:00</IssueDate>
<AssetList>
<Asset>
<Id>urn:uuid:c1eab1db-5218-46c7-a05f-0e304083f604</Id>
<PackingList>true</PackingList>
<ChunkList>
<Chunk>
<Path>PKL_c1eab1db-5218-46c7-a05f-0e304083f604.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>3063</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:393c1a9b-1907-4dd0-a3a9-5299b0dcdedf</Id>
<ChunkList>
<Chunk>
<Path>CPL_IMF_JOT_Sample_ML5HDR10_OV.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>22807</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:e6dda0de-d3b3-4132-b243-c5a32f906071</Id>
<ChunkList>
<Chunk>
<Path>VIDEO_e6dda0de-d3b3-4132-b243-c5a32f906071.mxf</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>14745242</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:56b010e4-f3e1-46f6-87e9-80b6ea099651</Id>
<ChunkList>
<Chunk>
<Path>AUDIO_56b010e4-f3e1-46f6-87e9-80b6ea099651.mxf</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>4341294</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:d669a9dc-bb65-4e6c-8ca5-8c4c7db78366</Id>
<ChunkList>
<Chunk>
<Path>AUDIO_d669a9dc-bb65-4e6c-8ca5-8c4c7db78366.mxf</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>1458414</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:f17087ab-50ea-4537-8d1b-f53b54bd8ea8</Id>
<ChunkList>
<Chunk>
<Path>SUB_f17087ab-50ea-4537-8d1b-f53b54bd8ea8.mxf</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>34509</Length>
</Chunk>
</ChunkList>
</Asset>
<Asset>
<Id>urn:uuid:809da04c-8daf-4835-9ae5-889e7f93d18e</Id>
<ChunkList>
<Chunk>
<Path>OPL_809da04c-8daf-4835-9ae5-889e7f93d18e.xml</Path>
<VolumeIndex>1</VolumeIndex>
<Offset>0</Offset>
<Length>725</Length>
</Chunk>
</ChunkList>
</Asset>
`
I like using dictionaries to group ids. Below I used xml linq to do all the parsing. Below is just a start but if you need additional help just let me know. I'm not sure which IDs need to match. The code below is not finding any matches.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static string[] cplFILENAMES = { #"c:\temp\test.xml" };
static string[] assetFILENAMES = { #"c:\temp\test1.xml" };
static void Main(string[] args)
{
foreach (string filename in cplFILENAMES)
{
new CompositionPlayList(filename);
}
foreach (string filename in assetFILENAMES)
{
new AssetMap(filename);
}
var groups = (from cpl in CompositionPlayList.dictSequences
join asset in AssetMap.dictAsset on cpl.Key equals asset.Key into a
from asset in a.DefaultIfEmpty()
select new { id = cpl.Key, cpl = cpl.Value, asset = asset.Value }).ToList();
}
}
public class CompositionPlayList
{
public string filename { get; set; }
public string id { get; set; }
public DateTime issueDate { get; set; }
public string segmentID { get; set; }
public static Dictionary<string, Sequence> dictSequences = new Dictionary<string, Sequence>();
public CompositionPlayList() { }
public CompositionPlayList(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement cpl = doc.Root;
XNamespace ns = cpl.GetDefaultNamespace();
this.filename = filename;
id = ((string)cpl.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault();
issueDate = (DateTime)cpl.Element(ns + "IssueDate");
XElement segment = cpl.Descendants(ns + "Segment").FirstOrDefault();
segmentID = ((string)segment.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault();
List<XElement> sequences = segment.Element(ns + "SequenceList").Elements().ToList();
XNamespace ccNs = sequences.FirstOrDefault().GetNamespaceOfPrefix("cc");
Dictionary<string, Sequence> newDictSequences = sequences.Select(x => new Sequence
{
sequenceType = x.Name.LocalName,
id = ((string)x.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault(),
trackID = ((string)x.Element(ns + "TrackId")).Split(new char[] { ':' }).LastOrDefault()
filename = filename;
}).GroupBy(x => x.trackID, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
foreach (KeyValuePair<string, Sequence> sequence in newDictSequences)
{
dictSequences.Add(sequence.Key, sequence.Value);
}
}
}
public class Sequence
{
public string sequenceType { get; set; }
public string id { get; set; }
public string trackID { get; set; }
public string filename { get; set; }
}
public class AssetMap
{
public string filename { get; set; }
public string id { get; set; }
public DateTime issueDate { get; set; }
public static Dictionary<string, Asset> dictAsset = new Dictionary<string, Asset>();
public AssetMap() { }
public AssetMap(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement assetMap = doc.Root;
XNamespace ns = assetMap.GetDefaultNamespace();
this.filename = filename;
id = ((string)assetMap.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault();
issueDate = (DateTime)assetMap.Element(ns + "IssueDate");
List<XElement> assets = assetMap.Element(ns + "AssetList").Elements().ToList();
Dictionary<string, Asset> newDictAsset = assets.Select(x => new Asset
{
id = ((string)x.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault()
}).GroupBy(x => x.id, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
foreach (KeyValuePair<string, Asset> sequence in newDictAsset)
{
dictAsset.Add(sequence.Key, sequence.Value);
}
}
}
public class Asset
{
public string id { get; set; }
}
}
I originally used a dictionary to make lookup quicker. Now that I'm using a left outer join the dictionary isn't needed so below is a more efficient version of the code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static string[] cplFILENAMES = { #"c:\temp\test.xml" };
static string[] assetFILENAMES = { #"c:\temp\test1.xml" };
static void Main(string[] args)
{
foreach (string filename in cplFILENAMES)
{
new CompositionPlayList(filename);
}
foreach (string filename in assetFILENAMES)
{
new AssetMap(filename);
}
var groups = (from cpl in CompositionPlayList.sequences
join asset in AssetMap.assets on cpl.trackID equals asset.id into a
from asset in a.DefaultIfEmpty()
select new { id = cpl.id, cpl = cpl.trackID, asset = asset}).ToList();
}
}
public class CompositionPlayList
{
public string filename { get; set; }
public string id { get; set; }
public DateTime issueDate { get; set; }
public string segmentID { get; set; }
public static List<Sequence> sequences = new List<Sequence>();
public CompositionPlayList() { }
public CompositionPlayList(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement cpl = doc.Root;
XNamespace ns = cpl.GetDefaultNamespace();
this.filename = filename;
id = ((string)cpl.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault();
issueDate = (DateTime)cpl.Element(ns + "IssueDate");
XElement segment = cpl.Descendants(ns + "Segment").FirstOrDefault();
segmentID = ((string)segment.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault();
List<XElement> sequences = segment.Element(ns + "SequenceList").Elements().ToList();
XNamespace ccNs = sequences.FirstOrDefault().GetNamespaceOfPrefix("cc");
List<Sequence> newSequences = sequences.Select(x => new Sequence {
sequenceType = x.Name.LocalName,
id = ((string)x.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault(),
trackID = ((string)x.Element(ns + "TrackId")).Split(new char[] { ':' }).LastOrDefault(),
filename = filename
}).ToList();
CompositionPlayList.sequences.AddRange(newSequences);
}
}
public class Sequence
{
public string sequenceType { get; set; }
public string id { get; set; }
public string trackID { get; set; }
public string filename { get; set; }
}
public class AssetMap
{
public string filename { get; set; }
public string id { get; set; }
public DateTime issueDate { get; set; }
public static List<AssetMap> assets = new List<AssetMap>();
public AssetMap() { }
public AssetMap(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement assetMap = doc.Root;
XNamespace ns = assetMap.GetDefaultNamespace();
this.filename = filename;
id = ((string)assetMap.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault();
issueDate = (DateTime)assetMap.Element(ns + "IssueDate");
List<XElement> assets = assetMap.Element(ns + "AssetList").Elements().ToList();
List<AssetMap> newAssets = assets.Select(x => new AssetMap() { id = ((string)x.Element(ns + "Id")).Split(new char[] { ':' }).LastOrDefault(), filename = filename, issueDate = issueDate }).ToList();
AssetMap.assets.AddRange(newAssets);
}
}
}
I have the following XML:
https://pastebin.com/YQBhNzm5
I want to match up the item values with the field values.
XmlDocument xdoc = new XmlDocument();
xdoc.Load(ofd.FileName);
XmlNamespaceManager xmanager = new XmlNamespaceManager(xdoc.NameTable);
xmanager.AddNamespace("ns", "http://www.canto.com/ns/Export/1.0");
var result = xdoc.SelectNodes("//ns:Layout/ns:Fields", xmanager);
foreach(XmlElement item in result)
{
Console.WriteLine(item.InnerText);
}
When I do this I get all the field names in one line. How can I iterate through all fields in layout and go one by one?
I parsed xml using xml linq. First I put items into a dictionary. Then I parsed the fields looking up the uid from the dictionary. I parsed the fields recursively to keep the hierarchy.
It looks like the uid, type, value, and name are always the same for each item, but an item can appear in multiple catalogs with a catalog id and an id.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Layout layout = new Layout(FILENAME);
}
}
public class Layout
{
public string tablename { get; set; }
public List<Field> fields { get; set; }
public Layout layout { get; set; }
public Dictionary<string, Item> dict = new Dictionary<string, Item>();
public Layout() { }
public Layout(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement xLayout = doc.Descendants().Where(x => x.Name.LocalName == "Layout").FirstOrDefault();
XNamespace ns = xLayout.GetNamespaceOfPrefix("ns");
foreach (XElement item in doc.Descendants(ns + "Item"))
{
int catalogid = (int)item.Attribute("catalogid");
int id = (int)item.Attribute("id");
foreach(XElement fieldValue in item.Elements(ns + "FieldValue"))
{
string uid = (string)fieldValue.Attribute("uid");
uid = uid.Replace("{", "");
uid = uid.Replace("}", "");
string innertext = (string)fieldValue;
string displayValue = (string)fieldValue.Attribute("displayValue");
List<string> categoryValues = fieldValue.Elements(ns + "CategoryValue").Select(x => (string)x).ToList();
if (!dict.ContainsKey(uid))
{
Item newItem = new Item() {
catalogidId = new List<KeyValuePair<int, int>>() {new KeyValuePair<int, int>(catalogid, id)},
innertext = innertext,
uid = uid,
displayValue = displayValue,
categoryValues = categoryValues
};
dict.Add(uid, newItem);
}
else
{
dict[uid].catalogidId.Add(new KeyValuePair<int, int>(catalogid, id));
}
}
}
layout = new Layout();
RecursiveParse(ns, xLayout, layout);
}
public void RecursiveParse(XNamespace ns, XElement parent, Layout layout)
{
layout.tablename = (string)parent.Attribute("tableName");
foreach(XElement xField in parent.Element(ns + "Fields").Elements(ns + "Field"))
{
if (layout.fields == null) layout.fields = new List<Field>();
Field newField = new Field();
layout.fields.Add(newField);
newField.uid = (string)xField.Attribute("uid");
newField.uid = newField.uid.Replace("{", "");
newField.uid = newField.uid.Replace("}", "");
newField._type = (int)xField.Attribute("type");
newField.value = (int)xField.Attribute("valueInterpretation");
newField.name = (string)xField.Element(ns + "Name");
if (dict.ContainsKey(newField.uid))
{
newField.items = dict[newField.uid];
}
if (xField.Element(ns + "Layout") != null)
{
Layout newLayout = new Layout();
newField.layout = newLayout;
RecursiveParse(ns, xField.Element(ns + "Layout"), newLayout);
}
}
}
public class Field
{
public string uid { get; set; }
public int _type { get; set; }
public int value { get; set; }
public string name { get; set; }
public Layout layout { get; set; }
public Item items { get; set; }
}
public class Item
{
public List<KeyValuePair<int, int>> catalogidId { get; set; }
public string uid { get; set; }
public string innertext { get; set; }
public string displayValue { get; set; }
public List<string> categoryValues { get; set; }
}
}
}