C# Linq XDoc - add element with same name - c#

I'm attempting to write a small XML file using c# Linq XDocument.
The final xml file should look like this:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Asset InternalID="SOMEID" LastSaveDate="2016-10-28" LastSaveTime="01:01:33:00" AssetType="New">
<type_metadata>
<FIELD name="filename">SOMEID.MOV</FIELD>
<FIELD name="duration">00:00:00:10</FIELD>
</type_metadata>
</Asset>
</Root>
Here is my code:
XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", null));
doc.Add(new XElement("Root"));
doc.Element("Root").Add(new XElement("Asset"));
doc.Element("Root").Element("Asset").Add(new XAttribute("InternalID", a.InternalID));
doc.Element("Root").Element("Asset").Add(new XAttribute("LastSaveDate", a.lastSaveDate));
doc.Element("Root").Element("Asset").Add(new XAttribute("LastSaveTime", a.lastSaveTime));
doc.Element("Root").Element("Asset").Add(new XAttribute("AssetType", a.AssetType));
doc.Element("Root").Element("Asset").Add(new XElement("type_metadata"));
doc.Element("Root").Element("Asset").Element("type_metadata").Add(new XElement("FIELD"));
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD").Add(new XAttribute("name","filename"));
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD").Value = a.filename;
doc.Element("Root").Element("Asset").Element("type_metadata").Add(new XElement("FIELD"));
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD").Add(new XAttribute("name", "duration"));
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD").Value = a.duration;
Everything works fine until I try to put in the second "FIELD" element.
What is the proper way to do this? I have done some research, but I cant find a simple answer that is directly relevant to what I'm trying to accomplish.

That's because when you're trying to add second element you're using:
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD")
It will return first element matching the name, which is this case is the previously added "FIELD" element, which already has "name" attribute.
I'd suggest you create the element itself before attaching it to the document. This way you won't have to search for the element over and over again:
XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", null));
var root = new XElement("Root");
var asset = new XElement("Asset");
asset.Add(new XAttribute("InternalID", a.InternalID));
asset.Add(new XAttribute("LastSaveDate", a.lastSaveDate));
asset.Add(new XAttribute("LastSaveTime", a.lastSaveTime));
asset.Add(new XAttribute("AssetType", a.AssetType));
var type_metadata = new XElement("type_metadata");
var field = new XElement("FIELD");
field.Add(new XAttribute("name","filename"));
field.Value = a.filename;
type_metadata.Add(field);
var field2 = new XElement("FIELD");
field2.Add(new XAttribute("name","duration"));
field2.Value = a.duration;
type_metadata.Add(field2);
asset.Add(type_metadata);
root.Add(asset);
doc.Add(root);
Also, you can create the entire document in a single statement:
XDocument doc = new XDocument(
new XDeclaration("1.0", "UTF-8", null),
new XElement("Root",
new XElement("Asset",
new XAttribute("InternalID", a.InternalID),
new XAttribute("LastSaveDate", a.lastSaveDate),
new XAttribute("LastSaveTime", a.lastSaveTime),
new XAttribute("AssetType", a.AssetType),
new Element("type_metadata",
new XElement("FIELD",
new XAttribute("name", "filename"),
a.filename),
new XElement("FIELD",
new XAttribute("name", "duration"),
a.duration)))));

It would be easier if you prepare new FIELD element before adding it to the parent element :
var filename = new XElement("FIELD",
new XAttribute("name","filename"),
a.filename
);
var duration = new XElement("FIELD",
new XAttribute("name","duration"),
a.duration
);
doc.Element("Root").Element("Asset").Element("type_metadata").Add(field);
doc.Element("Root").Element("Asset").Element("type_metadata").Add(duration);

This can be achieved in different ways. I just followed your approach. Since you have multiple FIELD elements, slight modification required in your code. The following code will work as expected.
XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", null));
doc.Add(new XElement("Root"));
doc.Element("Root").Add(new XElement("Asset"));
doc.Element("Root").Element("Asset").Add(new XAttribute("InternalID", "intID"));
doc.Element("Root").Element("Asset").Add(new XAttribute("LastSaveDate", "28.10.2016"));
doc.Element("Root").Element("Asset").Add(new XAttribute("LastSaveTime", "1.48PM"));
doc.Element("Root").Element("Asset").Add(new XAttribute("AssetType", "Laptop"));
doc.Element("Root").Element("Asset").Add(new XElement("type_metadata"));
doc.Element("Root").Element("Asset").Element("type_metadata").Add(new XElement("FIELD"));
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD").Add(new XAttribute("name", "filename"));
doc.Element("Root").Element("Asset").Element("type_metadata").Element("FIELD").Value = "a.txr";
doc.Element("Root").Element("Asset").Element("type_metadata").Add(new XElement("FIELD"));
doc.Element("Root").Element("Asset").Element("type_metadata").Elements().Last().Add(new XAttribute("name", "duration"));
doc.Element("Root").Element("Asset").Element("type_metadata").Elements().Last().Value = "00:12:98";

Related

XML add both a named and a default namespace error

I want to create an XML file with two namespaces for my root element, one default and one named. Following is my code:
var testdoc= new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Document",
new XAttribute("xmlns", "namespace1"),
new XAttribute(XNamespace.Xmlns + "xsi", "namespace2"),
new XElement("sampleElem", "content")
)
);
This generates the following error:
the prefix for "namespace2" can not be redefined within the same code for starting a new element.
I understand the error, but I do not understand why I get it (as the prefix name is not the same). Anyone know the correct way to get the desired result?
Because in this line new XElement("Document", you have already created an element with a namespace by default. Specifying the attribute you are trying to override it.
Do this
XNamespace ns = "namespace1";
var testdoc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(ns + "Document",
new XAttribute(XNamespace.Xmlns + "xsi", "namespace2"),
new XElement("sampleElem", "content")
)
);

Using XDocument and XElement how do I add a XAttribute and a value to XML in C#

I am trying to replicate:
<gcf>
<cbxDecOnly Type="Boolean">False</cbxDecOnly>
<cbxFormName Type="String" />
<txtCustomerCellPhonePart2 Type="String">5236</txtCustomerCellPhonePart2>
<txtCustomerCellPhonePart1 Type="String">533</txtCustomerCellPhonePart1>
....
</gcf>
so far I have:
var xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("gcf",
new XElement("cbxDecOnly", new XAttribute("Type", "Boolean")),
new XElement("cbxFormName", oGSFE.TextBoxClientName),
new XElement("txtCustomerCellPhonePart2", oGSFE.TextBoxDealSearch),
new XElement("txtCustomerCellPhonePart1 ", oGSFE.DropDownListFIManager)
)
);
what I don't know is how to add a XAttribute and a value at the same time to the XML element <cbxDecOnly Type="Boolean">False</cbxDecOnly>
In the same way you provide the value for your txtCustomerCellPhonePart2 etc nodes - by including the string value as one of the element's params content[]:
var xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("gcf",
new XElement("cbxDecOnly", "False", new XAttribute("Type", "Boolean")),
new XElement("cbxFormName", oGSFE.TextBoxClientName),
new XElement("txtCustomerCellPhonePart2", oGSFE.TextBoxDealSearch),
new XElement("txtCustomerCellPhonePart1", oGSFE.DropDownListFIManager)
)
);
Any values of type string provided in content[] will be merged into the element's value, any values of type XAttribute will create attributes and any values of type XElement will become the children.

Cannot add "xmlns" attribute to urlset in sitemap.xml

I generate a sitemap.xml
XDocument xDoc = new XDocument(
new XDeclaration("1.0", "UTF-8", ""),
new XElement("urlset",
new XAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9"),
new Element (....and so on...)
I get an error
The prefix '' cannot be redefined from '' to 'http://www.sitemaps.org/schemas/sitemap/0.9' within the same start element tag.
Google requires xmlns attribute without any prefix.
Seems that adding default namespace in XDocument is a bit tricky, related question : How to set the default XML namespace for an XDocument
You can try to declare default namespace prefix and use that prefix for all elements within <urlset> like so :
XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
XDocument xDoc = new XDocument(
new XDeclaration("1.0", "UTF-8", ""),
new XElement(ns+"urlset",
new XElement(ns+"otherElement"),
new XElement (....and so on...)
You can solve this problem by using blank namespace like the following:
XNamespace blank = XNamespace.Get(#"http://www.sitemaps.org/schemas/sitemap/0.9");
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(blank + "urlset",
new XAttribute("xmlns", blank.NamespaceName),
new XElement(blank + "loc", "http://www.abc.eba/"),
new Element (....and so on...)
));

How to add element along with its attributes using XDocument

Using XDocument i can add an element by
new XElement("elementName", "elementText");
and add an attribute by
new XAttribute("attributeName", "attributeValue");
However when i use the following code
XDocument doc =
new XDocument(
new XElement("Address", new XAttribute("name", "sample"))
);
there is no text being added for the element 'Address'
How to add both element and attribute at the same time?
You can pass string as another XElement constructor parameter, and it will be placed as element content:
XDocument doc =
new XDocument(
new XElement("Address",
new XAttribute("name", "sample"),
"elementText"
)
);
Calling doc.ToString() now gives <Address name="sample">elementText</Address>.
Just to let you know: it can be done with XText class too, but I think using plain string is more convenient:
XDocument doc =
new XDocument(
new XElement("Address",
new XAttribute("name", "sample"),
new XText("elementText")
)
);

How to wrtie a XML License Line(ended with a forward slash '/') in C#?

I want to write a XML file as below:
<?xml version="1.0" encoding="UTF-8"?>
<books xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<License licenseId="" licensePath="" />
Some piece of my code attached here
// Create a new file in D:\\ and set the encoding to UTF-8
XmlTextWriter textWriter = new XmlTextWriter("D:\\books.xml", System.Text.Encoding.UTF8);
// Format automatically
textWriter.Formatting = Formatting.Indented;
// Opens the document
textWriter.WriteStartDocument();
// Write the namespace declaration.
textWriter.WriteStartElement("books", null);
// Write the genre attribute.
textWriter.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
textWriter.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
And now I need to write the License Line below in C#
<License licenseId="" licensePath="" />
But I don't know how to move on for I found the Line ended with the forward slash / .Thank you.
I have 2 questions about the way you're doing this:
1) Do you have to use a text writer? If you have access to c# 3.0 then you can use the following:
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
new XElement("Equipment",
new XElement("License",
new XAttribute("licenseId", ""),
new XAttribute("licensePath", "")
)
)
);
2) Do you have to declare the two namespaces? It seems to me like you won't use them:
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Equipment",
new XElement("License",
new XAttribute("licenseId", ""),
new XAttribute("licensePath", "")
)
)
);
If you're intending to write multiple License elements to the document, and you have them in an Array, List or some other IEnumerable, you can use something similar to the code below to spit them all out:
IEnumerable<LicenceObjects> licenses = //some code to make them;
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Equipment",
licenses.Select(l =>
new XElement("License",
new XAttribute("licenseId", l.licenseId),
new XAttribute("licensePath", l.licensePath)
)
)
)
);
string xmlDocumentString = doc.ToString();
Of course, if you don't have .NET 3.0, then this is useless to you :(
Calling the WriteEndElement method will automatically take care of adding the forwards slash.
Why don't you just proceed as you started?
textWriter.WriteStartElement("Licence");
textWriter.WriteAttributeString("LicenseId", "");
textWriter.WriteAttributeString("LicensePath", "");
// Other stuff
textWriter.WriteEndDocument();
textWriter.Close();

Categories