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.
Related
I want to create xml using XElement as you can see:
XDocument RejectedXmlList = new XDocument
(
new XDeclaration("1.0", "utf-8", null)
);
int RejectCounter = 0;
foreach (Parameter Myparameter in Parameters)
{
if (true)
{
XElement xelement = new XElement(Myparameter.ParameterName, CurrentData.ToString());
RejectedXmlList.Add(xelement);
}
}
As you can see if the condition is OK the parameter should be added to RejectedXmlList but instead I get this exception:
This operation would create an incorrectly structured document.
Of note, the first parameter is added successfully. Only when the second one is added do I get the exception.
The expected result should be like this:
<co>2</co>
<o2>2</o2>
....
You are trying to create an XDocument with multiple root elements, one for each Parameter in Parameters You can't do that because the XML standard disallows it:
There is exactly one element, called the root, or document element, no part of which appears in the content of any other element.
The LINQ to XML API enforces this constraint, throwing the exception you see when you try to add a second root element to the document.
Instead, add a root element, e.g. <Rejectedparameters>, then add your xelement children to it:
// Allocate the XDocument and add an XML declaration.
XDocument RejectedXmlList = new XDocument(new XDeclaration("1.0", "utf-8", null));
// At this point RejectedXmlList.Root is still null, so add a unique root element.
RejectedXmlList.Add(new XElement("Rejectedparameters"));
// Add elements for each Parameter to the root element
foreach (Parameter Myparameter in Parameters)
{
if (true)
{
XElement xelement = new XElement(Myparameter.ParameterName, CurrentData.ToString());
RejectedXmlList.Root.Add(xelement);
}
}
Sample fiddle.
I am trying to extract some SQL data to XML from a Microsoft Dynamics environment, I am currently using LINQ To XML in C# to read and write to my XML files. One piece of data I need is from a view called SECURITYSUBROLE. Looking at the structure of this view shows that there is a column also named SECURITYSUBROLE. My normal method of extraction has given me this XML.
<SECURITYSUBROLE>
<SECURITYROLE>886301</SECURITYROLE>
<SECURITYSUBROLE>886317</SECURITYSUBROLE>
<VALIDFROM>1900-01-01T00:00:00-06:00</VALIDFROM>
<VALIDFROMTZID>0</VALIDFROMTZID>
<VALIDTO>1900-01-01T00:00:00-06:00</VALIDTO>
<VALIDTOTZID>0</VALIDTOTZID>
<RECVERSION>1</RECVERSION>
<RECID>886317</RECID>
</SECURITYSUBROLE>
When I try to import this data later on, I am getting errors because the parent XML node has the same name as a child node. Here is a snippet of the import method:
XmlReaderSettings settings = new XmlReaderSettings();
settings.CheckCharacters = false;
XmlReader reader = XmlReader.Create(path, settings);
reader.MoveToContent();
int count = 1;
List<XElement> xmlSubset = new List<XElement>();
while (reader.ReadToFollowing(xmlTag))
{
if (count % 1000 == 0)
{
xmlSubset.Add(XElement.Load(reader.ReadSubtree()));
XDocument xmlTemp = new XDocument(new XElement(xmlTag));
foreach (XElement elem in xmlSubset)
{
xmlTemp.Root.Add(elem);
}
xmlSubset = new List<XElement>();
ImportTableByName(connectionString, tableName, xmlTemp);
count = 1;
}
else
{
xmlSubset.Add(XElement.Load(reader.ReadSubtree()));
count++;
}
}
}
It's currently failing on the XmlReader.ReadToFollowing, where it doesn't know where to go next because of the name confusion. So my question has two parts:
1) Is there some better way to be extracting this data other than to XML?
2) Is there a way through LINQ To XML that I can somehow differentiate between the parent and child nodes named exactly the same?
To get the elements (in your case) for SECURITYSUBROLE you can check to see if the element's have children:
XElement root = XElement.Load(path);
var subroles = root.Descendants("SECURITYSUBROLE") // all subroles
.Where(x => !x.HasElements); // only subroles without children
I'm going to suggest a different approach:
1) VS2013 (possibly earlier versions too) has a function to create a class from an XML source. So get one of your XML files and copy the content to your clipboard. Then in a new class file Edit --> Paste Special --> Paste XML as Classes
2) Look into XmlSerialization which will allow you to convert an XML file into an in memory object with a strongly typed class.
XmlSerializer s = new XmlSerializer(yourNewClassTYPE);
TextReader r = new StreamReader(XmlFileLocation);
var dataFromYourXmlAsAStronglyTypedClass = (yourNewlyClassTYPE) s.Deserialize(r);
r.Close();
I'm new to XML & C#.
I want to remove root element without deleting child element.
XML file is strudctured as below.
<?xml version="1.0" encoding="UTF-8"?>
<dataroot generated="2013-07-06T20:26:48" xmlns:od="urn:schemas-microsoft-com:officedata">
<MetaDataSection>
<Name>KR04</Name>
<XMLCreationDate>02.05.2013 9:52:41 </XMLCreationDate>
<Address>AUTOMATIC</Address>
<Age>22</Age>
</MetaDataSection>
</dataroot>
I want to root element "dataroot", so it should look like below.
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataSection>
<Name>KR04</Name>
<XMLCreationDate>02.05.2013 9:52:41 </XMLCreationDate>
<Address>AUTOMATIC</Address>
<Age>22</Age>
</MetaDataSection>
Deleting child elements look like easy, but I don't know how to delete root element only.
Below is the code I've tried so far.
XmlDocument xmlFile = new XmlDocument();
xmlFile.Load("path to xml");
XmlNodeList nodes = xmlFile.SelectNodes("//dataroot");
foreach (XmlElement element in nodes)
{
element.RemoveAll();
}
Is there a way to remove root element only? without deleting child elements?
Thank you in advnace.
Rather than trying to actually remove the root element from an existing object, it looks like the underlying aim is to save (or at least access) the first child element of the root element.
The simplest way to do this is with LINQ to XML - something like this:
XDocument input = XDocument.Load("input.xml");
XElement firstChild = input.Root.Elements().First();
XDocument output = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
firstChild);
output.Save("output.xml");
Or if you don't need the XML declaration:
XDocument input = XDocument.Load("input.xml");
XElement firstChild = input.Root.Elements().First();
firstChild.Save("output.xml");
if you need to do in c# coding means
Solution
foreach (XElement item in Element.Descendants("dataroot").ToList())
{
item.ReplaceWith(item.Nodes());
}
I want to write a simple program to query TFS and convert all of the work items to a uniform type XML file and save them to separate files in a folder.
I'm sure this kind of work is commonly enough done, and is very simple - but I can find no samples on the Internet and no way of connecting to TFS programmatically and retrieving only work item info. Would anyone be able to help me out ?
Thanks very much
private TfsTeamProjectCollection GetTfsTeamProjectCollection()
{
TeamProjectPicker workitemPicker = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false, new UICredentialsProvider());
workitemPicker.AcceptButtonText = "workitemPicker.AcceptButtonText";
workitemPicker.Text = "workitemPicker.Text";
workitemPicker.ShowDialog();
if (workitemPicker.SelectedProjects != null || workitemPicker.SelectedProjects.Length > 0)
{
return workitemPicker.SelectedTeamProjectCollection;
}
return null;
}
private WorkItemCollection WorkItemByQuery(TfsTeamProjectCollection projects, string query) //query is likethis:SELECT [System.ID], [System.Title] FROM WorkItems WHERE [System.Title] CONTAINS 'Lei Yang'
{
WorkItemStore wis = new WorkItemStore(projects);
return wis.Query (query );
}
WorkItemCollection is what you want. You can get WorkItems and their properties.
You should use the TFS SDK.
You can find many tutorials on internet like this one.
MSDN will also help you too.
You can use the TFS SDK working with work items. Then all you need to do is convert it into the format you want.
You can fetch the query results as suggested by Lei Yang.
Then start building the XML.
XmlDocument xmlDoc = new XmlDocument();
//XML declaration
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
// Create the root element
XmlElement rootNode = xmlDoc.CreateElement("WorkItemFieldList");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);
//Create a new element and add it to the root node
XmlElement parentnode = xmlDoc.CreateElement("UserInput");
Iterate through all the workitem field values
xmlDoc.DocumentElement.PrependChild(parentnode);
//wiTrees of type WorkItemLinkInfo[] is the result of RunLinkQuery
foreach (var item in wiTrees)
{
int fieldcount = workItemStore.GetWorkItem(item.TargetId).Fields.Count;
while (fieldcount > 0)
{
//Create the required nodes
XmlElement mainNode = xmlDoc.CreateElement(workItemStore.GetWorkItem(item.TargetId).Fields[fieldcount -1].Name.ToString().Replace(" ", "-"));
// retrieve the text
//Use the custom method NullSafeToString to handle null values and convert them to String.Empty
XmlText categoryText = xmlDoc.CreateTextNode(workItemStore.GetWorkItem(item.TargetId).Fields[fieldcount - 1].Value.NullSafeToString().ToString());
// append the nodes to the parentNode without the value
parentnode.AppendChild(mainNode);
// save the value of the fields into the nodes
mainNode.AppendChild(categoryText);
fieldcount--;
}
}
// Save to the XML file
xmlDoc.Save("widetails.xml");
I have an existing XDocument object that I would like to add an XML doctype to. For example:
XDocument doc = XDocument.Parse("<a>test</a>");
I can create an XDocumentType using:
XDocumentType doctype = new XDocumentType("a", "-//TEST//", "test.dtd", "");
But how do I apply that to the existing XDocument?
You can add an XDocumentType to an existing XDocument, but it must be the first element added. The documentation surrounding this is vague.
Thanks to Jeroen for pointing out the convenient approach of using AddFirst in the comments. This approach allows you to write the following code, which shows how to add the XDocumentType after the XDocument already has elements:
var doc = XDocument.Parse("<a>test</a>");
var doctype = new XDocumentType("a", "-//TEST//", "test.dtd", "");
doc.AddFirst(doctype);
Alternately, you could use the Add method to add an XDocumentType to an existing XDocument, but the caveat is that no other element should exist since it has to be first.
XDocument xDocument = new XDocument();
XDocumentType documentType = new XDocumentType("Books", null, "Books.dtd", null);
xDocument.Add(documentType);
On the other hand, the following is invalid and would result in an InvalidOperationException: "This operation would create an incorrectly structured document."
xDocument.Add(new XElement("Books"));
xDocument.Add(documentType); // invalid, element added before doctype
Just pass it to the XDocument constructor (full example):
XDocument doc = new XDocument(
new XDocumentType("a", "-//TEST//", "test.dtd", ""),
new XElement("a", "test")
);
or use XDocument.Add (the XDocumentType has to be added before the root element):
XDocument doc = new XDocument();
doc.Add(new XDocumentType("a", "-//TEST//", "test.dtd", ""));
doc.Add(XElement.Parse("<a>test</a>"));