Add an existing xml element to a xml document - c#

In this LINQ query I am creating a new XDocument for my own CD with track objects:
var cdXml = new XDocument(new XElement("CD", new XAttribute("Artiest", c.Titel), new XAttribute("Naam", c.Naam),
from t in c.tracks
select new XElement("Tracks",
new XElement("Track"),
new XElement("Artiest", t.Artiest),
new XElement("Titel", t.Titel),
new XElement("Lengte", t.Lengte)
)));
I am using an API from a music website and this is how I created the second XDocument from it:
String xmlString;
using (WebClient wc = new WebClient())
{
xmlString = wc.DownloadString(#"http://link-to-method");
}
XDocument myXMLDoc = XDocument.Parse(xmlString);
After that I want to add the tracks from the myXMLDoc to my own cdXml document, I already added 3 tracks to the cdXml document and I only want to add the tracks from the myXMLDoc who aren't already in my cdXml document.
This is my query but it doesn't work:
var query1 = from track in cdXml.Root.Elements("Tracks")
from track2 in myXMLDoc.Root.Element("album").Element("tracks").Elements("track")
where !track2.Element("name").Value.Contains(track.Element("Titel").Value)
select cdXml.Element("Tracks").Add(new XElement("Artiest", track2.Element("name").Value),
new XElement("Titel", track2.Element("artist").Element("name").Value),
new XElement("Lengte", track2.Element("duration").Value));
How do I add the existing elements from the myXMLDoc to my cdXml?
This is the xml file from the API call from 2 tracks:
<lfm status="ok">
<album>
<name>Awake</name>
<artist>Dream Theater</artist>
<mbid>e5544c68-43e9-4754-9239-b618454557f4</mbid>
<url>https://www.last.fm/music/Dream+Theater/Awake</url>
<image size="small">https://lastfm-img2.akamaized.net/i/u/34s/96e5ac5821bf4a138aec1b8f80f25a6f.png</image>
<listeners>216679</listeners>
<playcount>6046178</playcount>
<tracks>
<track rank="1">
<name>6:00</name>
<url>https://www.last.fm/music/Dream+Theater/_/6:00</url>
<duration>331</duration>
<streamable fulltrack="0">0</streamable>
<artist>
<name>Dream Theater</name>
<mbid>28503ab7-8bf2-4666-a7bd-2644bfc7cb1d</mbid>
<url>https://www.last.fm/music/Dream+Theater</url>
</artist>
</track>
<track rank="2">
<name>Caught in a Web</name>
<url>https://www.last.fm/music/Dream+Theater/_/Caught+in+a+Web</url>
<duration>328</duration>
<streamable fulltrack="0">0</streamable>
<artist>
<name>Dream Theater</name>
<mbid>28503ab7-8bf2-4666-a7bd-2644bfc7cb1d</mbid>
<url>https://www.last.fm/music/Dream+Theater</url>
</artist>
</track>
</album>
</lfm>
I finally found my own solution:
var query = from track in myXMLDoc.Root.Element("album").Element("tracks").Elements("track")
join track2 in cdXml.Root.Elements("Tracks") on track.Element("name").Value equals track2.Element("Titel").Value into joinedT
from track2 in joinedT.DefaultIfEmpty()
where track2 == null
select track;

First, write a query that returns the elements you want to add to the second document. Just that. Write a query that returns those. In the debugger, confirm that you're getting what you want to get.
Then, write a foreach loop which goes over those elements, and adds each one to whatever you want to add them to.
Your attempt at using select to add things is impossible. You can't do that. Linq isn't how you add things to collections. It's just for writing queries.
I think this might be what you were trying to do, I don't understand the track/track2 thing in the first query.
var query1 =
from track in cdXml.Root.Elements("Tracks")
from track2 in myXMLDoc.Root.Element("album").Element("tracks").Elements("track")
where !track2.Element("name").Value.Contains(track.Element("Titel").Value)
select track2;
query1 = query1.ToList();
// Put a breakpoint HERE and examine query1 in the debugger to
// see what you got
;
foreach (var element in query1)
{
cdXml.Element("Tracks").Add(
new XElement("Artiest", element.Element("name").Value),
new XElement("Titel", element.Element("artist").Element("name").Value),
new XElement("Lengte", element.Element("duration").Value));
}

This is my solution :
XElement tracks = cdXml.Root.Element("Tracks");
List<XElement> query1 = myXMLDoc.Root.Element("album").Element("tracks").Elements("track")
.Where(track2 => track2.Element("name").Value.Contains(track.Element("Titel").Value)).ToList();
foreach (XElement q1 in query1)
{
tracks.Add(new XElement("Artiest", q1.Element("name").Value),
new XElement("Titel", q1.Element("artist").Element("name").Value),
new XElement("Lengte", q1.Element("duration").Value));
}

Related

Need to select Data from XML file C#

What I'm trying to do is get data from my XML file which has been merged with two others and selected each venue from that file and try to add the value to a list so I can manipulate it further.
This is one of my XML files
<?xml version="1.0" encoding="utf-8" ?>
<Funrun>
<Venue name="Roker Park">
<Runner charity="Cancer Research">
<Firstname>Roger</Firstname>
<Lastname>Malibu</Lastname>
<Sponsorship>550</Sponsorship>
</Runner>
<Runner charity="Arthritis UK">
<Firstname>Adam</Firstname>
<Lastname>White</Lastname>
<Sponsorship>340</Sponsorship>
</Runner>
</Venue>
</Funrun >
I need to be able to select the venue name and save it to a list. This is what I've got so far:
List<string> VenueNames = new List<string>();
var doc = XDocument.Load("XMLFile1.xml");
var doc2 = XDocument.Load("XMLFile2.xml");
var doc3 = XDocument.Load("XMLFile3.xml");
var combinedUnique = doc.Descendants("Venue")
.Union(doc2.Descendants("Venue"))
.Union(doc3.Descendants("Venue"));
foreach (var venuename in combinedUnique.Elements("Venue"))
{
VenueNames.Add(venuename.Attribute("name").Value));
}
The easiest way I would do it is by including Name and Charity within the XElements they belong to.
What I would recommend you do is first reformat your document so that it looks like this:
<Funrun>
<Venue>
<Name>Roker Park</Name>
<Runner1>
<charity>Cancer Research</charity>
<Firstname>Roger</Firstname>
<Lastname>Malibu</Lastname>
<Sponsorship>550</Sponsorship>
</Runner1>
<Runner2>
<charity>Arthritis UK</charity>
<Firstname>Adam</Firstname>
<Lastname>White</Lastname>
<Sponsorship>340</Sponsorship>
</Runner2>
</Venue>
</Funrun >
Note that you could get even simpler by combining all the elements under "Funrun" (example: "Venue") and just iterate through all of them without having to switch documents.
Next moving over to C#:
List<string> VenueNames = new List<string>();
var doc = XDocument.Load("XMLFile1.xml");
var doc2 = XDocument.Load("XMLFile2.xml");
var doc3 = XDocument.Load("XMLFile3.xml");
foreach (XElement element in doc.Root.Descendants("Venue"))
{
VenueNames.Add(element.Element("Name").Value.ToString());
}
//Copy paste this code for each document you would like to search through, though of course change "doc" to say, "doc2".
So just real quick, what this code will do is it will first open the Root element in the XDocument. It will find Decendants of that element with the name, "Name", and for each of those it will copy its value as a string into your list.
List<string> xmlFilePaths = new List<string>
{
#"Path\\SomeJson.txt",
#"Path\\SomeJson1.txt"
};
var venues = xmlFilePaths.Select(fp => XDocument.Load(fp).Descendants("Venue")?.FirstOrDefault()?.Attribute("name")?.Value).Distinct().ToList();

LINQ to XML, with conditions and Variable XML Structure with C#

i have an big issue, and just lost like 5 hours on it.
I have to create an XML file, which will be populated by a database reg.
i have variable structure, which in some cases will use specific sub structure.
the logic is simple.
I have to get all agencys,
then in every agency i have to iterate a foreach cycle,
and get all houses that they whant to sell/rent.
But its not only houses, thereĀ“s apartaments, garages, and so on.
Each of this scenes have their own structure with different data.
This is not all, there must have an condition. Only writes xml childs only if they are supposed to.
piece of xml example
<Clients>
<Client>
<aggregator/>
<code/>
<reference/>
<contact>
<secondhandListing>
<property>
<code/>
<reference/>
<scope/>
<address>
<features/>
<operation> // variable structure
1example <price/>
<communityCosts/>
2example <price/>
<communityCosts/>
<depositType/>
</operation>
</property>
</secondhandListing>
Can some one show me an example of how this could be done.
what I archive until now was:
var agenciasConectores = //query of Agencys
foreach (var agenciaConector in agenciasConectores)
{
var imoveisAgencia = // query of homes in each Agency
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Clients",
from agencia in agenciasConectores
select new XElement("Client", new XAttribute("ID", agencia.AgencyId),
new XElement("aggregator", agencia.ConnectorLicense),
new XElement("code", agencia.Name),
new XElement("Reference", agencia.Phone),
new XElement("contact", agencia.Phone),
new XElement("secondhandListing"),
new XElement("newbuildListing")
)));
foreach (var imovel in imoveisAgencia)
{
if (imoveisAgencia.Count() > 1)
{
doc.Document.Add(new XElement("property",
new XElement("code", "codigo"),
new XElement("reference", "reference"),
new XElement("scope", "scope"),
new XElement("address", "address"),
new XElement("contact", "contact"),
new XElement("features", "features"),
new XElement("operation", "operation"),
new XElement("description", "description")));
}
}
}
When I try to run this in Visual Studio, the following line throws an exception
doc.Document.Add(new XElement("property",
...
are you trying to do something more like this?
doc.Root.Add(new XElement("property",
...
or, perhaps, something closer to this
doc.Descendants("Client")
.Single(c => c.code == "something")
.Add(new XElement("property",
...
Already find a solution.
its simple.
XElement root = new XElement("root");
XElement child = new XElement("Child");
XElement child2 = new XElement(Child2);
then I populate the elements.
Child.SetValue(a);
child2.Setvalue(b);
and at final, i just add elements to parent Element.
root.add(Child,Child2);
But like that i also can make some validations(in my case in cycle)
If(a>b)
root.add(child);
else
root.add(child2);
thx for the try. I learn it by the example hehehhe

parsing a xml file which contains more than one keyvalues using c#

I have file format
<?xml version='1.0' encoding='us-ascii'?>
<root>
<file id="001">
<filename>ABC.wav</filename>
<value>0.18</value>
</file>
<file id="002">
<filename>EFG.wav</filename>
<value>0.05</value>
<value>0.14</value>
</file>
</root>
I want to parse that USING C#
doc.Load(confidencethresholdFilePath+"\\model.xml");
XmlNodeList nodes = doc.DocumentElement.SelectNodes("/root/file");
List<Result> results = new List<Result>();
foreach (XmlNode node in nodes)
{
Result result = new Result();
result.ASfilename= node.SelectSingleNode("filename").InnerText;
result.resultedSeconds = node.SelectSingleNode("value").InnerText;
results.Add(result);
}
It gives result but misses the second record's second value.How do i get all results without fail.
How about using LINQ to XML?
var xDoc = XDocument.Load("Input.xml");
var results =
xDoc.Root
.Elements("file")
.Select(f => new
{
FileName = (string)f.Element("filename"),
Values = f.Elements("value").Select(v => (string)v).ToList()
})
.ToList();
results will be a list of anonymous type instances with two properties: FileName:string and Values:List<string>. You can easily change it to return List<Record> instead, just change f => new to f => new Record and update properties info.
As you can see, it's much easier to get XML content using LINQ to XML than using old-style XmlSomething classes.
If you want a separate Result for each value and using your non-Linq style, you could change the initial selection. The file name selection will then need to change appropriately.
doc.Load(confidencethresholdFilePath+"\\model.xml");
XmlNodeList nodes = doc.DocumentElement.SelectNodes("/root/file/value");
List<Result> results = new List<Result>();
foreach (XmlNode node in nodes)
{
Result result = new Result();
result.ASfilename= node.SelectSingleNode("../filename").InnerText;
result.resultedSeconds = node.SelectSingleNode("value").InnerText;
results.Add(result);
}

Linq to XML dynamic XML Decendants

I'm parsing a lot of XML files using Linq to XML synatx, everything works when I try to access top level elements
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
model = (string)element.Element("MODEL"),
}).FirstOrDefault()
The problem occurs when I need to access lower level childs of that document I tried:
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
ddName = (string)element.Descendants("DD_NAME").Elements("name").First();
}).FirstOrDefault()
and
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name")
}).FirstOrDefault();
Sadly none of that works and i get same error "Sequence contains no elements". And one more thing sometimes the XML document contains those tags and sometimes not is something like this enough for handling this case?
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name") ?? "-"
}).FirstOrDefault();
Edit:
I don't think is possible to paste short version of XML that would be simple, so here's full version: http://pastebin.com/uDkP3rnR and for the code example:
XDocument prodcutDocument = XDocument.Load(this.ServerPATHData + file);
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
modelis = (string)element.Element("MODELIS"),
T2918_0 = (string)element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
}).FirstOrDefault();
writeTxt.WriteLine("modelis: " + indexroot.modelis);
writeTxt.WriteLine("T2979_0" + indexroot.T2918_0);
In examining the sample XML that you posted on PasteBin, it appears to me that the elements that you mention appear only once. To access them, you can simply specify a path to each as follows:
XElement indexroot = document.Root.Element("indexroot");
XElement modelis = indexroot.Element("MODELIS");
XElement dd_dgs = indexroot.Element("dd_DARBINIS_GRAFIKAS_SPEC");
XElement voltageuv = dd_dgs.Element("VoltageUV");
string t2979_0 = (string)voltageuv.Element("T2979_0");
string t2861_60 = (string)voltageuv.Element("T2861_60");
string t2757_121 = (string)voltageuv.Element("T2757_121");
(Note that you may need to check for null if there is a chance that any of the elements you are trying to access may not be present. Without doing so, you'll encounter a NullReferenceException.)
Here is a snippet of the XML that you posted to give context to the above code:
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<PDB>
<indexroot>
<ed_BENDRA_MAKS_SUV_GALIA>1.45</ed_BENDRA_MAKS_SUV_GALIA>
<ed_BENDRA_MAKS_SROVE>6.48</ed_BENDRA_MAKS_SROVE>
<TIPAS>1</TIPAS>
<MODELIS>RIS 2500 HW EC 3.0</MODELIS>
<dd_DARBINIS_GRAFIKAS_SPEC>
<VoltageUV>
<T2979_0>229,42</T2979_0>
<T2861_60>227,98</T2861_60>
<T2757_121>228,97</T2757_121>
</VoltageUV>
<CurrentIA>
<T2979_0>2,56</T2979_0>
<T2861_60>2,63</T2861_60>
<T2757_121>2,72</T2757_121>
</CurrentIA>
</dd_DARBINIS_GRAFIKAS_SPEC>
</indexroot>
</PDB>
You can just change:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
to this:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").FirstOrDefault() ?? "-"

LINQ Create new Xdocument by looping through existing XML file

I'm very new to LINQ so please bear with me :)
I'm currently trying to reverse engineer a SSIS package (.dtsx) which is in XML format into a .BIML file which is also XML based. However they have different constructs for the same objects.
So what i'm trying to do is to loop through the XML of the .dtsx package and have a basically check the type of the element and create an equivalent element in a new file but with a different name/attributes, however i will need to keep the hierarchical relationship of the objects as i create the elements in the new file.
But i'm really struggling with how i can add new elements to the new file whilst i'm looping through the source file.
Is anyone able to offer some pointers?
I'm able to loop through the file at the moment (i'm just outputting to a console window at teh moment to check understand if i'm looping corectly) but i'm struggling to add the elements into the new file
any help very much appreciated
string file = #"F:\\sample.dtsx"
XDocument xDoc = XDocument.Load(file);
XNamespace env = "www.microsoft.com/SqlServer/Dts";
IEnumerable<XElement> elements = xDoc.Root.Descendants(); //
XDocument BIMLXdoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("Root"));
//BIMLXdoc.Add(new XElement("test")); ####This doesn't work
foreach (XElement element in elements)
{
// Test element and if of the correct type add new elemnt to biml file
IEnumerable<XAttribute> attribs = element.Attributes();
foreach (XAttribute attrib in attribs)
{
Console.WriteLine(element.Name.LocalName + " - Attribute(" + attrib.Name.LocalName + ") - Value:(" + attrib.Value + ")");
}
}
BIMLXdoc.Save("F:\\BIMLTest.xml");
In commented line you are trying to add node to the top level. Correct XML document has only one root element. So, add your nodes to Root element:
var BIMLXdoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("Root"));
BIMLXdoc.Root.Add(new XElement("test")); // This works
If you want to add a new Element to your Xdoc, you need to add it in a specific place:
Change:
BIMLXdoc.Add(new XElement("test"));
To:
BIMLXdoc.Element("Root").Add(new XElement("TestChild", "TestChildValue"));
And you will have a child element.

Categories