<Family_Inventory>
<FamilyCategory>Curtain Panels
<FamilyName>System Panel
<FamilySymbol>Glazed</FamilySymbol>
<FamilySymbol>Wall</FamilySymbol>
</FamilyName>
</FamilyCategory>
<FamilyCategory>Curtain Panels
<FamilyName>Rectangular Mullion
<FamilySymbol>2.5" x 5" rectangular</FamilySymbol>
</FamilyName>
</FamilyCategory>
...........
...........// many other family categories
...........
</Family_Inventory>
i want create only one FamilyCategory tag based on its value as above is 'Curtain Panels'
so output will be
<Family_Inventory>
<FamilyCategory>Curtain Panels
<FamilyName>System Panel
<FamilySymbol>Glazed</FamilySymbol>
<FamilySymbol>Wall</FamilySymbol>
</FamilyName>
<FamilyName>Rectangular Mullion
<FamilySymbol>2.5" x 5" rectangular</FamilySymbol>
</FamilyName>
</FamilyCategory>
...........
...........// many other family categories
...........
</Family_Inventory>
Please tell me how Can i do this?.
Thanks in Advance
Regards,
Nitin
You should read the documentation on XmlDocument and XmlNode and it's associated classes. There is also plenty of examples of how to use them online.
Here is an introduction to XML with C#.
The first thing that comes to my mind is to use linq to get the whole document... then just create two classes
class FamilyCategory
{
string name;
List<FamilyName> familyNames;
}
class FamilyName
{
string name;
List<string> familySymbol;
}
after you parse the whole document just sort them by familyCategory.. then you just go through the list. You create a new instance of FamilyCategory and add all the FamilyNames to its list... For each new Cateogry you create a new class... after that you just need to add them all back which is described Here
I've not tested this, but I think it's close. It's a very manual approach, and there may be a slicker way to do it, but it's just manually grouping the categories into a new xdocument called groupedCategoryDocument.
var doc = XDocument.Parse(xmlString);
var groupedCategoryDocument = XDocument.Parse("<Family_Inventory />");
foreach(var category in doc.Root.Elements("FamilyCategory"))
{
//create the category for the new document
var groupedCategory = new XElement("FamilyCategory", category.Text);
//get all of the families for this category
foreach(var familyName in category.Elements("FamilyName"))
{
//add to new document
groupedCategory.Add(XElement.Parse(familyName.ToString());
}
// add category to new document
groupedCategoryDocument.Add(groupedCategory);
}
On third preview, I think the XML you are trying to generate is too complex and I think this will be the easiest both in terms of generation & consumption
<Family_Inventory>
<Family category="{category-name}" name="{family-name}" symbol="{symbol-1}"/>
<Family category="{category-name}" name="{family-name}" symbol="{symbol-2}"/>
...
<Family category="{category-name}" name="{family-name}" symbol="{symbol-n}"/>
</Family_Inventory>
One possibility is to use XSLT and grouping by keys. In this case it is complicated by the use of mixed content for "category" and "name"; it makes the key construction non-obvious:
normalize-space(./node()[1]) The first child node of FamilyCategory is assumed to be a text node that contains the category name,; it is further trimmed w.r.t. white space.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:key name="category" match="FamilyCategory" use="normalize-space(./node()[1])" />
<xsl:template match="Family_Inventory">
<xsl:copy>
<xsl:for-each
select="FamilyCategory[count(. | key('category', normalize-space(./node()[1]))[1]) = 1]">
<xsl:copy>
<xsl:value-of select="normalize-space(./node()[1])" />
<xsl:for-each select="key('category', normalize-space(./node()[1]))/FamilyName">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
A good explanation of how it works is found here: http://www.jenitennison.com/xslt/grouping/muenchian.xml
Related
I'm transforming a > 2GB file with a lookup template in the XSLT.
I would like this to run faster but can't find any low hanging fruit to improve performance. Any help would be greatly appreciated.
I'm a newb when it comes to transformations.
This is the current format of the XML file.
<?xml version="1.0" encoding="utf-8" ?>
<contacts>
<contact>
<attribute>
<name>text12</name>
<value>B00085590</value>
</attribute>
<attribute>
<name>text34</name>
<value>Atomos</value>
</attribute>
<attribute>
<name>date866</name>
<value>02/21/1991</value>
</attribute>
</contact>
<contact>
<attribute>
<name>text12</name>
<value>B00058478</value>
</attribute>
<attribute>
<name>text34</name>
<value>Balderas</value>
</attribute>
<attribute>
<name>date866</name>
<value>11/24/1997</value>
</attribute>
</contact>
</contacts>
The xslt I used for the transformation.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<!--Identify location of the lookup xml-->
<xsl:param name="lookupDoc" select="document('C:\Projects\Attributes.xml')" />
<!--Main Template-->
<xsl:template match="/contacts">
<!--Apply Formatted Contacts Template-->
<xsl:apply-templates select="contact" />
</xsl:template>
<!--Formatted Contacts Template-->
<xsl:template match="contact">
<contact>
<xsl:for-each select="attribute">
<!--Create variable to hold New Name after passing the Data Name to the Lookup Template-->
<xsl:variable name="newName">
<xsl:apply-templates select="$lookupDoc/attributes/attribute">
<xsl:with-param name="nameToMatch" select="name" />
</xsl:apply-templates>
</xsl:variable>
<!--Format Contact Element with New Name variable-->
<xsl:element name="{$newName}">
<xsl:value-of select="value"/>
</xsl:element>
</xsl:for-each>
</contact>
</xsl:template>
<!--Lookup Template-->
<xsl:template match="attributes/attribute">
<xsl:param name="nameToMatch" />
<xsl:value-of select='translate(translate(self::node()[name = $nameToMatch]/mappingname, "()*%$##!~<>'&,.?[]=-+/\:1234567890", "")," ","")' />
</xsl:template>
</xsl:stylesheet>
Sample Lookup XML
<?xml version="1.0" encoding="utf-8" ?>
<attributes>
<attribute>
<name>text12</name>
<mappingname>ID</mappingname>
<datatype>Varchar2</datatype>
<size>30</size>
</attribute>
<attribute>
<name>text34</name>
<mappingname>Last Name</mappingname>
<datatype>Varchar2</datatype>
<size>30</size>
</attribute>
<attribute>
<name>date866</name>
<mappingname>DOB</mappingname>
<datatype>Date</datatype>
<size></size>
</attribute>
</attributes>
Transformed XML
<?xml version="1.0" encoding="utf-8" ?>
<contacts>
<contact>
<ID>B00085590</ID>
<LastName>Brady</LastName>
<DOB>02/21/1991</DOB>
</contact>
<contact>
<ID>B00058478</ID>
<LastName>Balderas</LastName>
<DOB>11/24/1997</DOB>
</contact>
</contacts>
C#
XsltSettings settings = new XsltSettings(true, true);
XslCompiledTransform ContactsXslt = new XslCompiledTransform();
ContactsXslt.Load(#"C:\Projects\ContactFormat.xslt", settings, new XmlUrlResolver());
using (XmlReader r = XmlReader.Create(#"C:\Projects\Contacts.xml")){
using (XmlWriter w = XmlWriter.Create(#"C:\Projects\FormattedContacts.xml")) {
w.WriteStartElement("contacts");
while (r.Read()) {
if (r.NodeType == XmlNodeType.Element && r.Name == "contact") {
XmlReader temp = new XmlTextReader(new StringReader(r.ReadOuterXml()));
ContactsXslt.Transform(temp, null, w);
}
}
}
}
The approach I'm taking is transforming 1 node at a time to avoid an OutOfMemoryException. Should I be feeding larger chunks through to speed up the process? Or am I going about this all wrong?
I think you can simplify the XSLT code
<xsl:for-each select="attribute">
<!--Create variable to hold New Name after passing the Data Name to the Lookup Template-->
<xsl:variable name="newName">
<xsl:apply-templates select="$lookupDoc/attributes/attribute">
<xsl:with-param name="nameToMatch" select="name" />
</xsl:apply-templates>
</xsl:variable>
using the template
<xsl:template match="attributes/attribute">
<xsl:param name="nameToMatch" />
<xsl:value-of select='translate(translate(self::node()[name = $nameToMatch]/mappingname, "()*%$##!~<>'&,.?[]=-+/\:1234567890", "")," ","")' />
</xsl:template>
to
<xsl:for-each select="attribute">
<!--Create variable to hold New Name after passing the Data Name to the Lookup Template-->
<xsl:variable name="newName">
<xsl:apply-templates select="$lookupDoc/attributes/attribute[name = current()/name]"/>
</xsl:variable>
with the template being simplified to
<xsl:template match="attributes/attribute">
<xsl:value-of select='translate(translate(mappingname, "()*%$##!~<>'&,.?[]=-+/\:1234567890", "")," ","")' />
</xsl:template>
I think that for sure is a more concise and XSLT way of expressing the approach, whether it improves performance is something you would have to test.
In general with XSLT to improve performance of cross-references/lookups it is recommended to use a key so you would use
<xsl:key name="att-lookup" match="attributes/attribute" use="name"/>
and then use it as
<xsl:variable name="name" select="name"/>
<xsl:variable name="newName">
<!-- in XSLT 1 we need to change the context doc for the key lookup -->
<xsl:for-each select="$lookupDoc">
<xsl:apply-templates select="key('att-lookup', $name)"/>
</xsl:variable>
I think that would considerable speed up the lookup in a single transformation, as you combine XmlReader and XSLT to run the XSLT many times on as many elements your XmlReader finds I can't tell whether it helps a lot, you would need to try.
As pointed out in the XSLT 3 suggestion, I would also consider transforming the lookup file first and once to avoid the repetition of all those translate calls to create proper XML element names. Either do that outside of the existing XSLT or do it inside by using a variable and then exsl:node-set to convert the result tree fragment into a variable. But in your case as you run the XSLT repeatedly I think it is probably better to first transform the lookup document outside of the main XSLT, to avoid having to do all those translates again and again.
When reading huge xml files always use XmlReader. I like using a combination of XmlReader and Xml linq. I also like using dictionaries. See code below :
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
while (!reader.EOF)
{
if (reader.Name != "contact")
{
reader.ReadToFollowing("contact");
}
if (!reader.EOF)
{
XElement xContact = (XElement)XElement.ReadFrom(reader);
Contact newContact = new Contact();
Contact.contacts.Add(newContact);
newContact.attributes = xContact.Descendants("attribute")
.GroupBy(x => (string)x.Element("name"), y => (string)y.Element("value"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}
}
public class Contact
{
public static List<Contact> contacts = new List<Contact>();
public Dictionary<string, string> attributes { get; set; }
}
}
As an alternative, you might want to look into solving the task with XSLT 3 and its streaming feature (https://www.w3.org/TR/xslt-30/#streaming-concepts) as there you could process the huge input file in a forwards only but declarative way where you only in the template for the attribute element you need to ensure you work with a intentionally created full copy of that element to allow XPath navigation to the child elements. Additionally I think it makese sense to read in the lookup document only once and do the translate calls to create the proper element names only once. So the following is a streaming XSLT 3 solution runnable with Saxon 9.8 EE which transforms the lookup document into an XPath 3.1 map (https://www.w3.org/TR/xpath-31/#id-maps) and otherwise uses a streamable mode to process the large, main input:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="xs map"
version="3.0">
<!-- could of course load the document using select="document('lookup.xml')" instead of inlining it as done here just for the example and testing -->
<xsl:param name="lookup-doc">
<attributes>
<attribute>
<name>text12</name>
<mappingname>ID</mappingname>
<datatype>Varchar2</datatype>
<size>30</size>
</attribute>
<attribute>
<name>text34</name>
<mappingname>Last Name</mappingname>
<datatype>Varchar2</datatype>
<size>30</size>
</attribute>
<attribute>
<name>date866</name>
<mappingname>DOB</mappingname>
<datatype>Date</datatype>
<size></size>
</attribute>
</attributes>
</xsl:param>
<xsl:variable
name="lookup-map"
as="map(xs:string, xs:string)"
select="map:merge(
$lookup-doc/attributes/attribute
!
map {
string(name) : translate(translate(mappingname, '()*%$##!~<>''&,.?[]=-+/\:1234567890', ''), ' ','')
}
)"/>
<xsl:mode on-no-match="shallow-copy" streamable="yes"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="contact/attribute">
<xsl:variable name="attribute-copy" select="copy-of()"/>
<xsl:element name="{$lookup-map($attribute-copy/name)}">
<xsl:value-of select="$attribute-copy/value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Online sample (there running with Saxon 9.8 HE which ignores the streaming and does normal XSLT processing) is at https://xsltfiddle.liberty-development.net/bFDb2Ct/1.
To run streaming XSLT 3 with Saxon 9.8 and C# you use http://saxonica.com/html/documentation/dotnetdoc/Saxon/Api/Xslt30Transformer.html and set up ApplyTemplates on an input Stream with your huge input XML (http://saxonica.com/html/documentation/dotnetdoc/Saxon/Api/Xslt30Transformer.html#ApplyTemplates(System.IO.Stream,Saxon.Api.XmlDestination)).
I have been trying to write a utility in C# which takes an XML file, removes the xmlns attributes from the tags, sets the prefix of these attributes in the root tag, and then uses these prefixes in the tags instead.
Source XML file:
<?xml version="1.0" encoding="utf-8"?>
<Main version="1.0" xmlns="urn:root:v1">
<Report>
<Title>Some Value</Title>
</Report>
<Content>
<Address>
<CountryName xmlns="urn:location:v2">Australia</CountryName>
</Address>
</Content>
</Main>
Target XML file:
<?xml version="1.0" encoding="utf-8"?>
<root:Main version="1.0" xmlns:root="urn:root:v1" xmlns:loc="urn:location:v2">
<root:Report>
<root:Title>Some Value</root:Title>
</root:Report>
<root:Content>
<root:Address>
<loc:CountryName>Australia</loc:CountryName>
</root:Address>
</root:Content>
</root:Main>
I've managed to get part of the way there with the following code. I have replaced all tags with no attributes with the root prefix, and added the xmlns attribute to the root tag, but have not been successful with removing the xmlns attribute from CountryName tag and using the prefix there instead.
XDocument doc = XDocument.Load(#"C:\Temp\Source.xml");
var content = XElement.Parse(doc.ToString());
content.Attributes("xmlns").Remove();
content.Add(new XAttribute(XNamespace.Xmlns + "root", "urn:root:v1"));
content.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2"));
foreach (var node in doc.Root.Descendants().Where(n => n.Name.NamespaceName == "urn:location:v2"))
{
node.Attribute("xmlns").Remove();
node.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2"));
}
content.Save(#"C:\Temp\Target.xml");
Any help would be appreciated - thanks!
You're not a million miles away. All you need to do is remove any existing namespace declaration attributes and then add the ones you want to the root. The rest will be taken care of.
var doc = XDocument.Load(#"C:\Temp\Source.xml");
doc.Descendants().Attributes().Where(x => x.IsNamespaceDeclaration).Remove();
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "root", "urn:root:v1"));
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2"));
doc.Save(#"C:\Temp\Target.xml");
See this fiddle for a demo.
Consider XSLT, the special-purpose language designed to transform XML files. While I personally do not know or use C#, I do know it can run XSLT 1.0 scripts. See answers here. Also, the XSLT processor you choose to use must allow the document() function for this solution.
XSLT (save as .xsl file; notice namespaces declared in header)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:root="urn:root:v1" xmlns:local="urn:location:v2">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="root:{name()}" namespace="urn:root:v1">
<xsl:copy-of select="document('')/*/namespace::local"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="*[local-name()='CountryName']">
<xsl:element name="local:{name()}" namespace="urn:location:v2">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
C# (see link above)
var myXslTrans = new XslCompiledTransform();
myXslTrans.Load("XSLTScript.xsl");
myXslTrans.Transform("Input.xml", "Output.xml");
XML Output
<?xml version="1.0"?>
<root:Main xmlns:root="urn:root:v1" xmlns:local="urn:location:v2" version="1.0">
<root:Report>
<root:Title>Some Value</root:Title>
</root:Report>
<root:Content>
<root:Address>
<local:CountryName>Australia</local:CountryName>
</root:Address>
</root:Content>
</root:Main>
I have a lot of XML files that need to be edited.
I need to find all instances with:
Example
<Btn>
<sText>Hold</sText>
and add a field before it
<Btn>//star of new fields
<sText>Tools</sText>
*rest of fields*
</Btn> //end of added fields
<Btn> //start of original search
<sText>Hold</sText>
I have read the using regex on XML is not advisable. What would be the best way to achieve a large one time operation for multiple files for something like this?
For regex I tried but with no luck to just start out with searching for the needed fields.
/<Btn>(.*?(\n))+.*?<sText>Hold</sText>/im
Using editors like notepad++,Brackets currently to edit files. Any suggestions on doing a large one time one time operation would be greatly appreciated. Doing the changes by hand in the GUI to hundreds of configs is not desirable.Just looking for an alternative route to save sanity.
You can create an object for your XML document. From there you can traverse through all of its nodes, find what you are looking for and add them to a list. When you already have the list, you can then write your logic for inserting the nodes that you want. I'm using LINQ.
public class Program
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load("YourXmlFile.xml");
RootElement root = new RootElement(doc.Elements().FirstOrDefault());
foreach (XElement item in root.GetInstances())
{
//--Your logic for adding the fields you want
}
Console.ReadLine();
}
}
public class RootElement
{
public List<XElement> childElements { get; set; }
public RootElement(XElement xElement)
{
childElements = new List<XElement>();
foreach (XElement e in xElement.Elements())
{
childElements.Add(e);
}
}
public List<XElement> GetInstances()
{
List<XElement> instances = new List<XElement>();
foreach (XElement item in childElements)
{
if (item.Name == "Btn")
{
IEnumerable<XElement> elements = item.Elements();
XElement child = elements.FirstOrDefault(x => x.Name == "sText");
if (child != null)
{
if (child.Value == "Hold")
{
instances.Add(item);
}
}
}
}
return instances;
}
}
I have an XSL approach you might like to try. XSL is great for transforming XML documents of one kind into another (amongst other things).
As I understand it, you need to find each instance of Btn and copy it to a new instance before its current location.
With this in mind, here's how I got it to work.
Test.xml file:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="Test.xslt"?>
<Something>
<Btn>
<sText>Hold</sText>
<Another>Foo</Another>
</Btn>
<Btn>
<sText>Hold</sText>
</Btn>
<Btn>
<sText>Hold</sText>
</Btn>
</Something>
Note the use of the stylesheet reference, you would need to add this to the documents you wish to edit.
Test.xslt file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="Output">
<xsl:apply-templates select="//Btn" />
</xsl:element>
</xsl:template>
<xsl:template match="Btn">
<xsl:element name="NewBtn">
<xsl:copy-of select="current()/*" />
</xsl:element>
<xsl:element name="Btn">
<xsl:copy-of select="current()/*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The output should look like this:
<?xml version="1.0" encoding="utf-8"?>
<Output>
<NewBtn>
<sText>Hold</sText>
<Another>Foo</Another>
</NewBtn>
<Btn>
<sText>Hold</sText>
<Another>Foo</Another>
</Btn>
<NewBtn>
<sText>Hold</sText>
</NewBtn>
<Btn>
<sText>Hold</sText>
</Btn>
<NewBtn>
<sText>Hold</sText>
</NewBtn>
<Btn>
<sText>Hold</sText>
</Btn>
</Output>
The newly duplicated instances of your Btn nodes are named NewBtn in this example.
Note that I've changed/added some elements here (Output, Something) in order to get valid XML.
I hope that helps!
You can try to solve it without regular expressions. For example you can use XmlReader and XmlWriter
Read one row with XmlReader
Check for your condition
Skip/modify row
Write row with XmlWriter
It's the most memory and CPU efficient solution since you don't need to load whole file to memory and XML Writer/Reader in C# are pretty fast compared to XDocument or other fancy xml parsers. Moreover it's simple so you need to mess with regexes and can contain any complicated logic you will need.
I'm working with C# .Net 3.5 and trying to convert a given xml (XDocument) into an empty one (where XElement.IsEmpty would be true) containing no text values . I tried setting the XElement.Value to String.Empty but that results in <element><element> which isn't what i need. I needed it to be <element />. Can someone suggest how this can be done in .NET.
below is input example:
<Envelope>
<Body>
<Person>
<first>John</first>
<last>Smith</last>
<address>123</address>
</Person>
</Body>
<Envelope>
expected output:
<Envelope>
<Body>
<Person>
<first />
<last />
<address />
</Person>
</Body>
<Envelope>
You can use ReplaceWith() function to replace desired elements with empty elements :
var xml = #"<Envelope>
<Body>
<Person>
<first>John</first>
<last>Smith</last>
<address>123</address>
</Person>
</Body>
</Envelope>";
var doc = XDocument.Parse(xml);
foreach (XElement propertyOfPerson in doc.XPathSelectElements("/Envelope/Body/Person/*").ToList())
{
propertyOfPerson.ReplaceWith(new XElement(propertyOfPerson.Name.LocalName));
}
Console.WriteLine(doc.ToString());
Result :
In Interest of sharing and whilst i have accepted the answer above, i actually went with the below approach and using A XSLT to transform the XML into what i wanted, so using the below code:
//an XSLT which removes the values and stripes the white spaces
const string xslMarkup = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"> <xsl:output method=\"xml\" omit-xml-declaration=\"yes\" indent=\"no\"/> <xsl:strip-space elements=\"*\"/> <xsl:template match=\"#* | node()\"> <xsl:copy> <xsl:apply-templates select=\"#* | node()\"/> </xsl:copy> </xsl:template> <xsl:template match=\"node()|#*\"> <xsl:copy> <xsl:apply-templates select=\"node()|#*\"/> </xsl:copy> </xsl:template><xsl:template match=\"*/text()\"/> </xsl:stylesheet>";
var transformedXml = new XDocument();
XNode xml = YOUR_XML_OBJECT_HERE;
using (var writer = transformedXml.CreateWriter())
{
// Load the XSLT
var xslt = new XslCompiledTransform();
xslt.Load(XmlReader.Create(new StringReader(xslMarkup)));
// Execute the transform and output the results to a writer.
xslt.Transform(xml.CreateReader(), writer);
}
return transformedXml.ToString(SaveOptions.DisableFormatting);
Try creating a new XElement without a value:
var xElement = new XElement("Envelope", new XElement("Body", new XElement("Person", "")))
In that manner.
How can I remove the "xmlns:..." namespace information from each XML element in C#?
Zombiesheep's cautionary answer notwithstanding, my solution is to wash the xml with an xslt transform to do this.
wash.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" encoding="UTF-8"/>
<xsl:template match="/|comment()|processing-instruction()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
From here http://simoncropp.com/working-around-xml-namespaces
var xDocument = XDocument.Parse(
#"<root>
<f:table xmlns:f=""http://www.w3schools.com/furniture"">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>");
xDocument.StripNamespace();
var tables = xDocument.Descendants("table");
public static class XmlExtensions
{
public static void StripNamespace(this XDocument document)
{
if (document.Root == null)
{
return;
}
foreach (var element in document.Root.DescendantsAndSelf())
{
element.Name = element.Name.LocalName;
element.ReplaceAttributes(GetAttributes(element));
}
}
static IEnumerable GetAttributes(XElement xElement)
{
return xElement.Attributes()
.Where(x => !x.IsNamespaceDeclaration)
.Select(x => new XAttribute(x.Name.LocalName, x.Value));
}
}
I had a similar problem (needing to remove a namespace attribute from a particular element, then return the XML as an XmlDocument to BizTalk) but a bizarre solution.
Before loading the XML string into the XmlDocument object, I did a text replacement to remove the offending namespace attribute. It seemed wrong at first as I ended up with XML that could not be parsed by the "XML Visualizer" in Visual Studio. This is what initially put me off this approach.
However, the text could still be loaded into the XmlDocument and I could output it to BizTalk fine.
Note too that earlier, I hit one blind alley when trying to use childNode.Attributes.RemoveAll() to remove the namespace attribute - it just came back again!