I have an xml file as given below.
<?xml version="1.0" encoding="utf-8"?>
<file:Situattion xmlns:file="test">
<file:Properties>
</file:Situattion>
I would like to add the child element file:Character using xDocument.So that my final xml would be like given below
<?xml version="1.0" encoding="utf-8"?>
<file:Situattion xmlns:file="test">
<file:Characters>
<file:Character file:ID="File0">
<file:Value>value0</file:Value>
<file:Description>
Description0
</file:Description>
</file:Character>
<file:Character file:ID="File1">
<file:Value>value1</file:Value>
<file:Description>
Description1
</file:Description>
</file:Character>
</file:Characters>
Code in c# i tried using Xdocument class is given below.
XNamespace ns = "test";
Document = XDocument.Load(Folderpath + "\\File.test");
if (Document.Descendants(ns + "Characters") != null)
{
Document.Add(new XElement(ns + "Character"));
}
Document.Save(Folderpath + "\\File.test");
At line "Document.Add(new XElement(ns + "Character"));", I am getting an error:
"This operation would create an incorrectly structured document.".
How can I add the node under "file:Characters".
You're trying to add an extra file:Character element directly into the root. You don't want to do that - you want to add it under the file:Characters element, presumably.
Also note that Descendants() will never return null - it will return an empty sequence if there are no matching elements. So you want:
var ns = "test";
var file = Path.Combine(folderPath, "File.test");
var doc = XDocument.Load(file);
// Or var characters = document.Root.Element(ns + "Characters")
var characters = document.Descendants(ns + "Characters").FirstOrDefault();
if (characters != null)
{
characters.Add(new XElement(ns + "Character");
doc.Save(file);
}
Note that I've used more conventional naming, Path.Combine, and also moved the Save call so that you'll only end up saving if you've actually made a change to the document.
Document.Root.Element("Characters").Add(new XElement("Character", new XAttribute("ID", "File0"), new XElement("Value", "value0"), new XElement("Description")),
new XElement("Character", new XAttribute("ID", "File1"), new XElement("Value", "value1"), new XElement("Description")));
Note: I have not included the namespace for brevity. You have to add those.
Related
I am trying to figure out how to modify custom XML parts previusly saved in Excel. All the web resources I have found so far explain how to add custom XML parts in Excel. This I already know. But I want to modify existing parts.
The API seems to have only Add method. If Add method is called again it adds additional XML parts.
I use the following code to save my custom XML
XNamespace NS = "http://schema.blabla.com";
var xDoc = new XDocument(
new XDeclaration("1.0", "utf-8", "no"),
new XComment("Custom XML Parts demo"),
new XElement(NS + "demo",
new XElement(NS + "config",
new XElement(NS + "property",
new XAttribute("value", "myVlaue",
new XAttribute("key", "myKey"))))));
Office.CustomXMLPart customXMLPart = workbook.CustomXMLParts.Add(xDoc.ToString(), System.Type.Missing);
I use the following code to retrieve my custom XML
var retrievedXMLParts = workbook.CustomXMLParts.SelectByNamespace(NS.NamespaceName);
//FirstOrDefault always returns first saved data, LastOrDefault needs to be called to get the latest
//var customXMLPart = retrievedXMLParts.Cast<CustomXMLPart>().FirstOrDefault();
var customXMLPart = retrievedXMLParts.Cast<CustomXMLPart>().LastOrDefault();
var propertiesXML = customXMLPart != null ? customXMLPart.XML : String.Empty;
What I would like to achieve is to check if a custom XML exists update its content instead off adding it as duplicate
I think I have found a solution but it involves iterating through all the custom XML parts, deleting the one you want to update and then add again:
IEnumerator e = workbook.CustomXMLParts.GetEnumerator();
CustomXMLPart p;
while (e.MoveNext())
{
p = (CustomXMLPart) e.Current;
//p.BuiltIn will be true for internal buildin excel parts
if (p != null && !p.BuiltIn && p.NamespaceURI == NS.NamespaceName)
p.Delete();
}
When I add new nodes to the root of a node that has a namespace defined, subsequent nodes added all receive xmlns="" attached to to them. This is the code that shows my problem:
void Main()
{
var xdoc = new XDocument();
var shipmentRoot = new XElement("{someNS}Shipment");
var newElement = new XElement("ContainerCollection", new XElement("Container", new XElement("ContainerNumber", "42")));
newElement.SetAttributeValue("Content", "Partial");
shipmentRoot.Add(newElement);
xdoc.Add(shipmentRoot);
xdoc.Dump();
}
Generates this XML:
<Shipment xmlns="someNS">
<ContainerCollection Content="Partial" xmlns="">
<Container>
<ContainerNumber>42</ContainerNumber>
</Container>
</ContainerCollection>
</Shipment>
My desired XML would be:
<Shipment xmlns="someNS">
<ContainerCollection Content="Partial">
<Container>
<ContainerNumber>42</ContainerNumber>
</Container>
</ContainerCollection>
</Shipment>
While I suspect this is a duplicate, I can't easily find it. The problem is that in your desired XML, the ContainerCollection, Container and ContainerNumber elements are in the namespace "someNS" as that is the default inherited from their Shipment ancestor... but you're creating elements with an empty namespace. The fix is just to create them with the right namespace:
// I prefer this over using {someNS}Shipment, personally. YMMV.
XNamespace ns = "someNS";
var shipmentRoot = new XElement(ns + "Shipment");
var newElement = new XElement(ns + "ContainerCollection",
new XElement(ns + "Container",
new XElement(ns + "ContainerNumber", "42")));
newElement.SetAttributeValue("Content", "Partial");
Hello I'm trying to write a string like :
<xhtml:link rel="alternate" hreflang="de" href="http://www.example.com/de" />
using XmlTextWriter class
I've tried this piece of code:
// Write Alternative links
_writer.WriteStartElement("xhtml:link");
_writer.WriteAttributeString("rel","alternate");
_writer.WriteAttributeString("hreflang", "de");
_writer.WriteAttributeString("href", "http://example.com/de");
_writer.WriteEndElement();
Which generates this error:
Namespace prefix xhtml on link is not defined
But I don't need any namespaces provided for xhtml:link
Question: How to achieve the string that I need using XmlTextWriter?
Update 1: I have changed to LINQ to XML
But for now I have another problem... For the beginning I'll show the code:
private readonly XNamespace nsXhtml = "http://www.w3.org/1999/xhtml";
private readonly XNamespace nsSitemap = "http://www.sitemaps.org/schemas/sitemap/0.9";
private readonly XNamespace nsXsi = "http://www.w3.org/2001/XMLSchema-instance";
private readonly XNamespace nsLocation = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd";
public XDocument Generate()
{
var sitemap = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
var urlSet = new XElement(nsSitemap + "urlset",
new XAttribute("xmlns", nsSitemap),
new XAttribute(XNamespace.Xmlns + "xhtml", nsXhtml),
new XAttribute(XNamespace.Xmlns + "xsi", nsXsi),
new XAttribute(nsXsi + "schemaLocation", nsLocation),
from node in GenerateUrlNodes() // Provides a collection of "objects", actually it doesn't matter since we anyway convert them to XElement below...
select WriteUrlLocation(node.Url,node.UpdateFrequency,node.LastModified));
sitemap.Add(urlSet);
return sitemap;
}
protected XElement WriteUrlLocation(string url, UpdateFrequency updateFrequency, DateTime lastUpdated)
{
var urlNode = new XElement(nsSitemap + "url",
new XElement(nsSitemap + "loc", url),
new XElement(nsSitemap + "changefreq", updateFrequency),
new XElement(nsSitemap + "lastmod", lastUpdated)
);
var linkNode = new XElement(nsXhtml + "link",
new XAttribute("rel", "alternate"),
new XAttribute("hreflang", "de"),
new XAttribute("href", "http://example.com/de"));
urlNode.Add(linkNode);
return urlNode;
}
The problem is that When I inspect the Generated sitemap at Controller:
public ActionResult Sitemap()
{
var sitemap = _sitemapGenerator.Generate().ToString();
return Content(sitemap,"text/xml");
}
The whole xml is not as expected and, the <xhtml:link> element is rendered with a non-empty closing tag (thus I don't know if this is a problem here) .. Look at the image please
Update 2: Solved! Seems that the XML structure is valid but the browser is not displaying it right...
You should change to use a different overload of XmlWriter.StartElement. For example:
_writer.WriteStartElement("link", "http://www.w3.org/1999/xhtml");
That assumes you've already got a prefix alias of xhtml for the namespace http://www.w3.org/1999/xhtml. I'd still recommend shifting to use LINQ to XML as soon as you can though... XmlWriter is great for cases where you really need to stream the data (e.g. when it's huge) but otherwise, LINQ to XML makes things a lot easier:
XNamespace xhtml = "http://www.w3.org/1999/xhtml";
var element = new XElement(xhtml + "link",
new XAttribute("rel", "alternate"),
new XAttribute("hreflang", "de"),
new XAttribute("href", "http://example.com/de"));
parent.Add(element);
If you will use XML Writer and write this
<xhtml:link rel="alternate" hreflang="en" href="www.yoursite.com" />
you can choose this code for .NET CORE 2.0:
foreach (SitemapNodeAlternate a in alternate)
{
MyWriter.WriteStartElement("xhtml", "link", null);
MyWriter.WriteAttributeString("rel", "alternate");
MyWriter.WriteAttributeString("href", a.href);
MyWriter.WriteAttributeString("hreflang", a.hreflang);
MyWriter.WriteEndElement();
}
I need to create an XmlDocument partly by using old XML and partly by creating new. The problem is that the old XML contains custom namespaces and I can't seem to be able to use them as I get an XmlException. I've tried to add the namespace to many different places but I can't get over the Exception!
The Exception
System.Xml.XmlException was unhandled by user code
Message='my' is an undeclared prefix. Line 1, position 42.
Source=System.Xml
My Code
XmlDocument doc = new XmlDocument();
XmlSchema schema = new XmlSchema();
schema.Namespaces.Add("my", "http://foobar.com/");
doc.Schemas.Add(schema);
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(dec);
XmlElement root = doc.CreateElement("root");
root.SetAttribute("xmlns:my", "http://foobar.com/");
doc.AppendChild(root);
foreach (var item in GetItems())
{
XmlElement elem = doc.CreateElement("item");
elem.SetAttribute("id", item.id);
// Append body to elem
XmlElement body = doc.CreateElement("body");
body.InnerXml = item.Body; // Here is where I get the exception
elem.AppendChild(body);
// Append elem to root
root.AppendChild(elem);
}
Input from Item.Body is similar to
<aaa><bbb my:attr="55">Foo</bbb></aaa>
I expected the output to be similar to
<?xml version="1.0" encoding="utf-8"?>
<root my:attr="http://foobar.com/">
<item id="12345">
<body>
<aaa>
<bbb my:attr="55">Foo</bbb>
</aaa>
</body>
</item>
</root>
I'm open to alternatives to using this method. After I create the XmlDocument I prettyprint it, validate it against a schema and then push it out for the user to see.
The following is a workaround, best I can come up with:
XNamespace my = "http://foobar.com/";
var doc = new XDocument(new XElement("root",
new XAttribute(XNamespace.Xmlns + "my", my)));
var body = new XElement("body");
doc.Root.Add(new XElement("item", new XAttribute("id", 12345), body));
string innerItem = #"<aaa><bbb my:attr=""55"">Foo</bbb></aaa>";
string itemWrap = #"<wrap xmlns:my=""http://foobar.com/"">" + innerItem + "</wrap>";
XElement item = XElement.Parse(itemWrap);
body.Add(item.Element("aaa"));
Console.WriteLine(doc);
I'm trying to write an XML file that will be picked up and parsed by another service. In order for this to happen the XML must be formatted in a very specific way, namely:
<?xml version="1.0"?>
<Feedbacks:Feedbacks xmlns:Feedbacks="Feedbacks">
<Feedbacks:Elements>
<Feedback:XMLFeedback xmlns:Feedback="Feedback">
<Feedback:MfgUnitID></Feedback:MfgUnitID>
<Feedback:MachineId></Feedback:MachineId>
<Feedback:OperationCode></Feedback:OperationCode>
<Feedback:ItemSeqNum></Feedback:ItemSeqNum>
<Feedback:OperDispositionCd></Feedback:OperDispositionCd>
<Feedback:ItemId></Feedback:ItemId>
<Feedback:ParentItemId></Feedback:ParentItemId>
<Feedback:ItemEndSize>1821</Feedback:ItemEndSize>
<Feedback:ItemDispositionCd></Feedback:ItemDispositionCd>
<Feedback:OperStartDate></Feedback:OperStartDate>
<Feedback:OperEndDate></Feedback:OperEndDate>
</Feedback:XMLFeedback>
</Feedbacks:Elements>
</Feedbacks:Feedbacks>
with data of course between the innermost elements. Here's the issue though, no matter what I do, I can't get any of the C# classes to keep the semicolons on the innermost nodes. As far as I know these need to stay, so is there a way in C# to force it to format the nodes this way? I've tried all of the create methods that I could find in the XMLDocument class. I can get the outer nodes formatted fine, but the inner ones just keep creating problems.
Edit, sorry here's the code that makes the inner nodes.
private void AppendFile(string filename, string[] headers, Dictionary<string, string> values)
{
XmlDocument doc = new XmlDocument();
doc.Load(filename);
XmlNode node = doc.GetElementsByTagName(headers[headers.Length - 2]).Item(0);
string[] hPieces = headers[headers.Length - 1].Split(':');
XmlElement appendee = doc.CreateElement(hPieces[0].Trim(), hPieces[1].Trim(), hPieces[0].Trim());
node.AppendChild(appendee);
foreach (KeyValuePair<string, string> pair in values)
{
string[] ePieces = pair.Key.Split(':');
//XmlElement element = doc.CreateElement(ePieces[0].Trim(), string.Empty, ePieces[1].Trim());
//XmlText text = doc.CreateTextNode(pair.Value);
XmlNode innerNode = doc.CreateNode(XmlNodeType.Element, ePieces[1].Trim(), ePieces[0].Trim());
node.InnerText = pair.Value;
// element.AppendChild(text);
appendee.AppendChild(innerNode);
}
doc.Save(filename);
}
The data for the inner nodes comes in as key value pairs in the dictionary. Where the keys contain the intended name.
Edit2: This is what the file output looks like
<?xml version="1.0" encoding="utf-8"?>
<Feedbacks:Feedbacks xmlns:Feedbacks="Feedbacks">
<Feedbacks:Elements>
<Feedback:XMLFeedback xmlns:Feedback="Feedback">
<MfgUnitID></MfgUnitID>
<MachineId></MachineId>
<OperationCode</OperationCode>
<ItemSeqNum></ItemSeqNum>
<OperDispositionCd></OperDispositionCd>
<ItemId></ItemId>
<ParentItemId></ParentItemId>
<ItemEndSize></ItemEndSize>
<ItemDispositionCd></ItemDispositionCd>
<OperStartDate></OperStartDate>
<OperEndDate></OperEndDate>
</Feedback:XMLFeedback>
</Feedbacks:Elements>
</Feedbacks:Feedbacks>
You can accompish this easily with XLinq:
using System.Xml.Linq;
XNamespace ns1 = "Feedbacks";
XNamespace ns2 = "Feedback";
var doc = new XElement("Feedbacks",
new XAttribute(XNamespace.Xmlns+"Feedbacks", ns1));
doc.Add(new XElement(ns1 + "Elements",
new XElement(ns2 + "Feedback",
new XAttribute(XNamespace.Xmlns+"Feedback", ns2),
new XElement(ns2 + "Unit"))));
Gives
<Feedbacks xmlns:Feedbacks="Feedbacks">
<Feedbacks:Elements>
<Feedback:Feedback xmlns:Feedback="Feedback">
<Feedback:Unit />
</Feedback:Feedback>
</Feedbacks:Elements>
</Feedbacks>
Although I believe that your own output should be valid XML, relying on the parent namespcae.