c# Return All Array Values Not Found - c#

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);
}
}
}

Related

CSV with headers to XML in C#

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; }
}
}

C# Parse items from namespace

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; }
}
}
}

Read XML from c#

I'm trying to read a xml file from a c# application. so far no luck at all. This is the XML file
<?xml version="1.0" encoding="utf-8"?>
<ExportJobs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<JobList>
<Job Id="555555">
<Comments></Comments>
<DueDate>2017-11-17</DueDate>
<FormattedDueDate>17-Nov-2017 12:00</FormattedDueDate>
<TargetDueDate>2017-11-17</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Miss Ash</TenantName>
<Uprn>testUpr</Uprn>
<HouseName></HouseName>
</Job>
<Job Id="666666">
<Comments></Comments>
<DueDate>2018-03-15</DueDate>
<FormattedDueDate>15-Mar-2018 12:00</FormattedDueDate>
<TargetDueDate>2018-03-15</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Mr Howard</TenantName>
<Uprn>testUpr2</Uprn>
</Job>
</JobList>
</ExportJobs>
I'm trying to get the job Id and the Uprn from the joblist node and pass the values in to Sql Server DB. I tried this, but I can't get the values,
string costCode;
string uprn;
//File path where the xml is located
string filepath = "C:\\ExportJobs.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filepath);
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
costCode = node.Attributes["Id"].InnerText;
uprn = node.Attributes["Uprn"].InnerText;
}
I really appreciate any help. Thanks
XmlSerializer is your friend:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
public class ExportJobs
{
public List<Job> JobList { get; } = new List<Job>();
}
public class Job
{
[XmlAttribute]
public int Id { get; set; }
public string Comments { get; set; }
public DateTime DueDate { get; set; }
public string FormattedDueDate { get; set; }
public DateTime TargetDueDate{ get; set; }
public int ServiceTypeId { get; set; }
public string ServiceType { get; set; }
public string TenantName { get; set; }
public string Uprn { get; set; }
public string HouseName { get; set; }
}
static class P
{
static void Main()
{
var ser = new XmlSerializer(typeof(ExportJobs));
ExportJobs jobs;
using (var sr = new StringReader(xml))
{
jobs = (ExportJobs) ser.Deserialize(sr);
}
foreach(var job in jobs.JobList)
{
Console.WriteLine($"{job.Id} / {job.Uprn}: {job.DueDate}");
}
}
const string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<ExportJobs xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<JobList>
<Job Id=""555555"">
<Comments></Comments>
<DueDate>2017-11-17</DueDate>
<FormattedDueDate>17-Nov-2017 12:00</FormattedDueDate>
<TargetDueDate>2017-11-17</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Miss Ash</TenantName>
<Uprn>testUpr</Uprn>
<HouseName></HouseName>
</Job>
<Job Id=""666666"">
<Comments></Comments>
<DueDate>2018-03-15</DueDate>
<FormattedDueDate>15-Mar-2018 12:00</FormattedDueDate>
<TargetDueDate>2018-03-15</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Mr Howard</TenantName>
<Uprn>testUpr2</Uprn>
</Job>
</JobList>
</ExportJobs>";
}
You are accessing ChildNodes of root element, which contains only Jobs element, which in order does not contains attributes Id and Uprn.
The usual practice is to use XPath query as following:
foreach (XmlNode node in xmlDoc.DocumentElement.SelectNodes("Jobs/Job"))
{
costCode = node.Attributes["Id"].InnerText;
uprn = node.SelectSingleNode("Uprn").InnerText;
}
Note that Uprn is the node and not the node attribute.
Here is tested code. You need the namespace. See code below which is using xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication67
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement exportJobs = doc.Root;
XNamespace ns = exportJobs.GetDefaultNamespace();
var results = exportJobs.Descendants(ns + "Job").Select(x => new {
id = (string)x.Attribute(ns + "Id"),
comment = (string)x.Element(ns + "Comments"),
dueDate = (DateTime)x.Element(ns + "DueDate"),
formattedDueDate = (DateTime)x.Element(ns + "FormattedDueDate"),
targetDueDate = (DateTime)x.Element(ns + "TargetDueDate"),
serviceTypeId = (int)x.Element(ns + "ServiceTypeId"),
serviceType = (string)x.Element(ns + "ServiceType"),
tenantName = (string)x.Element(ns + "TenantName"),
uprn = (string)x.Element(ns + "Uprn"),
houseName = (string)x.Element(ns + "HouseName")
}).ToList();
}
}
}
I think the best way to solve your problem is XDocument class.
XDocument xDoc = XDocument.Load(#"D:\1.xml");
foreach(var node in xDoc.Descendants("Job"))
{
id = node.Attribute("Id");
foreach(var subnode in node.Descendants("Uprn"))
{
uprn = subnode.Value;
}
//or like that. but check it for null before
uprn = node.Descendants("Uprn")?.First().Value
}

How can i filter XmlNodeList

I have a set of data xml node list, i want to filter with a particular attributes inner text, I tried with this but nothing worked. My code here and also posting the xml data
string baseName = categoryName.Split(':').Last();
int categoryId = 0;
string xmlFile = File.ReadAllText(Application.StartupPath + #"\EbayCategories\EbayCategories.xml");
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlFile);
XmlNodeList nodeList = xmldoc.SelectNodes("/CategoryArray/Category[CategoryName='" + baseName + "']");
if (nodeList.Count > 0)
{
var memberNames = nodeList.Cast<XmlNode>().Where(node => node.Attributes["CategoryID"].InnerText == "58193").ToList();
categoryId = int.Parse(nodeList[0]["CategoryID"].InnerText);
}
Here is my xml Data. I want to filter CategoryParentID = My Value.
<CategoryArray>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>20081</CategoryID>
<CategoryLevel>1</CategoryLevel>
<CategoryName>Antiques</CategoryName>
<CategoryParentID>20081</CategoryParentID>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>37903</CategoryID>
<CategoryLevel>2</CategoryLevel>
<CategoryName>Antiquities</CategoryName>
<CategoryParentID>20081</CategoryParentID>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>37908</CategoryID>
<CategoryLevel>3</CategoryLevel>
<CategoryName>The Americas</CategoryName>
<CategoryParentID>37903</CategoryParentID>
<LeafCategory>true</LeafCategory>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>162922</CategoryID>
<CategoryLevel>3</CategoryLevel>
<CategoryName>Byzantine</CategoryName>
<CategoryParentID>37903</CategoryParentID>
<LeafCategory>true</LeafCategory>
</Category>
I have done with small changes Removed Attributes
var node = nodeList.Cast<XmlNode>().Where(n => n["CategoryParentID"].InnerText == "58193").Select(x => x["CategoryID"].InnerText).SingleOrDefault();
Perfectly worked!!!
Using Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Category.categories = doc.Descendants("Category").Select(x => new Category() {
BestOfferEnabled = (Boolean)x.Element("BestOfferEnabled"),
AutoPayEnabled = (Boolean)x.Element("BestOfferEnabled"),
CategoryID = (int)x.Element("CategoryID"),
CategoryLevel = (int)x.Element("CategoryLevel"),
CategoryName = (string)x.Element("CategoryName"),
CategoryParentID = (int)x.Element("CategoryParentID"),
}).ToList();
int id = 37903;
Category categoryId = Category.categories.Where(x => x.CategoryParentID == id).FirstOrDefault();
}
}
public class Category
{
public static List<Category> categories { get; set; }
public Boolean BestOfferEnabled { get; set; }
public Boolean AutoPayEnabled { get; set; }
public int CategoryID { get; set; }
public int CategoryLevel { get; set; }
public string CategoryName { get; set; }
public int CategoryParentID { get; set; }
}
}

Need Parse XML to List<Object>, name field are separated from data

I am implementing a windows service and i need to consume a WebService with REST, I want to parse this to List but i dont know how.
My problem is that the names of the fields are separated from the data.
The structure I get is this:
<?xml version="1.0" encoding="utf-8" ?>
<xml>
<result>OK</result>
<headers>
<header>lastname</header>
<header>firstname</header>
<header>Age</header>
</headers>
<data>
<datum>
<item>Kelly</item>
<item>Grace</item>
<item>33</item>
</datum>
</data>
</xml>
You can use XmlSerializer to deserialize that XML into c# classes that reflect its structure. For instance:
[XmlRoot("xml")] // Indicates that the root element is named "xml"
public class XmlResponse
{
[XmlElement("result")] // Indicates that this element is named "result"
public string Result { get; set; }
[XmlArray("headers")] // Indicates two-level list with outer element named "headers" and inner elements named "header"
[XmlArrayItem("header")]
public List<string> Headers { get; set; }
[XmlArray("data")] // Indicates two-level list with outer element named "data" and inner elements named "datum"
[XmlArrayItem("datum")]
public List<XmlResponseDatum> Data { get; set; }
}
public class XmlResponseDatum
{
[XmlElement("item")] // Indicates a one-level list with repeated elements named "item".
public List<string> Items { get; set; }
}
Which you could deserialize like:
public static T LoadFromXML<T>(string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
object result = new XmlSerializer(typeof(T)).Deserialize(reader);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
public static string GetXml<T>(T obj)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj);
return textWriter.ToString();
}
}
public static void Test()
{
string xml = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<xml>
<result>OK</result>
<headers>
<header>lastname</header>
<header>firstname</header>
<header>Age</header>
</headers>
<data>
<datum>
<item>Kelly</item>
<item>Grace</item>
<item>33</item>
</datum>
</data>
</xml>";
var response = LoadFromXML<XmlResponse>(xml);
Debug.WriteLine(GetXml(response));
As an alternative, here is some XDocument and using objects that do not need to be Xml decorated.
I advocate this approach because it is easier to tweak the XDocument/Xpath statements in case the structure of the Xml changes.....as opposed to the XmlAttributes.
This also allows the same objects to be used, even if there are different xml streams hydrating it. You just write a different XDocument "shredder" for each Xml Input.
[DebuggerDisplay("MyNotUsedStringKey = {MyNotUsedStringKey}")]
public class ImportParent
{
public ImportParent()
{
this.MyNotUsedStringKey = Guid.NewGuid().ToString("N");
this.ImportChildren = new ImportChildCollection();
}
public ImportChildCollection ImportChildren { get; set; }
public string MyNotUsedStringKey { get; set; }
}
public class ImportChildCollection : List<ImportChild>
{
public ImportChildCollection() { }
public ImportChildCollection(IEnumerable<ImportChild> src)
{
if (null != src)
{
foreach (ImportChild item in src)
{
item.Ordinal = this.Count + 1;
base.Add(item);
}
}
//AddRange(src);
}
}
[DebuggerDisplay("MyStringKey = {MyStringKey}, MyStringValue='{MyStringValue}', Ordinal='{Ordinal}'")]
public class ImportChild
{
public ImportChild()
{
}
public int Ordinal { get; set; }
public string MyStringKey { get; set; }
public string MyStringValue { get; set; }
}
string xmlString = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<xml>
<result>OK</result>
<headers>
<header>lastname</header>
<header>firstname</header>
<header>Age</header>
</headers>
<data>
<datum>
<item>Kelly</item>
<item>Grace</item>
<item>33</item>
</datum>
</data>
</xml> ";
XDocument xDoc = XDocument.Parse(xmlString);
//XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");
string ns = string.Empty;
List<ImportParent> parentKeys = new List<ImportParent>
(
from list in xDoc.Descendants(ns + "xml")
from item1 in list.Elements(ns + "headers")
where item1 != null
select new ImportParent
{
//note that the cast is simpler to write than the null check in your code
//http://msdn.microsoft.com/en-us/library/bb387049.aspx
ImportChildren = new ImportChildCollection
(
from detail in item1.Descendants("header")
select new ImportChild
{
MyStringKey = detail == null ? string.Empty : detail.Value
}
)
}
);
List<ImportParent> parentValues = new List<ImportParent>
(
from list in xDoc.Descendants(ns + "xml")
from item1 in list.Elements(ns + "data")
from item2 in item1.Elements(ns + "datum")
where item1 != null && item2 != null
select new ImportParent
{
//note that the cast is simpler to write than the null check in your code
//http://msdn.microsoft.com/en-us/library/bb387049.aspx
ImportChildren = new ImportChildCollection
(
from detail in item1.Descendants("item")
select new ImportChild
{
MyStringValue = detail == null ? string.Empty : detail.Value
}
)
}
);
/*Match up the Keys to the Values using "Ordinal" matches*/
foreach (ImportParent parent in parentKeys)
{
foreach (ImportChild child in parent.ImportChildren)
{
ImportChild foundMatch = parentValues.SelectMany(x => x.ImportChildren).Where(c => c.Ordinal == child.Ordinal).FirstOrDefault();
if (null != foundMatch)
{
child.MyStringValue = foundMatch.MyStringValue;
}
}
}
foreach (ImportParent parent in parentKeys)
{
foreach (ImportChild child in parent.ImportChildren)
{
Console.WriteLine("Key={0}, Value={1}", child.MyStringKey, child.MyStringValue);
}
}

Categories