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!
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 am working in Visual Studio 2012 with C#.
I have two xslt files.
One has a few templates.
Another has some nodes defined there.
All i want is to build a function in C# using which i pass the template name. Using that name it search in the one xslt and if there is a template with the given name, it copies it over into the second xslt.
F("GetMonth") should result as following:
XSLT1:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="GetMonth">
<xsl:param name="Month"/>
<xsl:param name="PutCall"/>
<xsl:value-of select ="'A'"/>
</xsl:template>
XSLT2:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<DocumentElement>
// Some Code written
</DocumentElement>
</xsl:template>
</xsl:stylesheet>
Resultant XSLT2:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="GetMonth">
<xsl:param name="Month"/>
<xsl:param name="PutCall"/>
<xsl:value-of select ="'A'"/>
</xsl:template>
<xsl:template match="/">
<DocumentElement>
// Some Tags defined here
</DocumentElement>
</xsl:template>
</xsl:stylesheet>
My attempt:
XmlDocument xslDoc1 = new XmlDocument();
XmlDocument xslDoc2 = new XmlDocument();
xslDoc1.Load("XSLT1.xslt");
xslDoc2.Load("XSLT2.xslt");
XmlNamespaceManager nsMg1r = new XmlNamespaceManager(xslDoc1.NameTable);
nsMgr1.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
XmlNamespaceManager nsMgr2 = new XmlNamespaceManager(xslDoc2.NameTable);
nsMgr2.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
XmlNodeList template = (XmlNodeList)xslDoc.SelectNodes("/xsl:stylesheet/xsl:template[#name = templateName]", nsMgr);
if(template != null)
{
// What code should be written here???
}
Please suggest.
string templateName = "GetMonth";
XmlNode template = xslDoc.SelectSingleNode(string.Format("/xsl:stylesheet/xsl:template[#name = '{0}']", templateName), nsMgr);
if (template != null)
{
// will append the template as last child of xsl:stylesheet
xslDoc2.DocumentElement.AppendChild(xslDoc2.ImportNode(template, true));
// as alternative to insert as the first child use
// xslDoc2.DocumentElement.InsertBefore(xslDoc2.ImportNode(template, true), xslDoc2.DocumentElement.FirstChild);
// now Save
xslDoc2.Save("XSLT2.xslt");
}
I am struggling with understanding an XSLT transformation. Currently I receive a serialized object in XML format, then apply XSLT and send new XML back the program. But now I need to eliminate XSLT step and to do the transformation internally in the program. The problem is I've seen XSLT sheet for the second time. Transformation sheet look really simmple but I still cannot undersnad what is going on there.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="http://tempuri.org/">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template name="CopyEverything" match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/t:Data/Flagged">
<xsl:element name="Flagged">
<xsl:apply-templates select="/t:Data/Covers/node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="/t:Data/FlaggedDetails">
<xsl:element name="FlaggedDetails">
<xsl:apply-templates select="/t:Data/TotalFlaggedDetails/node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="/t:Data/System/RArray">
<xsl:element name="{local-name()}">
<xsl:for-each select="/t:Data/System/RArray/Elem">
<xsl:call-template name="CopyEverything"/>
</xsl:for-each>
<xsl:for-each select="/t:Data/Elem/Elem">
<xsl:variable name="currentCode" select="Code" />
<xsl:variable name="showAlways" select="ShowAlways" />
<xsl:if test="count(/t:Data/System/RArray/Elem[Code=$currentCode])=0">
<xsl:call-template name="CopyEverything"/>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
As far as I understand something from Data/Flagged and Data/FlaggedDetails is copied to Data/System/RArray, I cannot translate this logic to C#. I have to eliminate serialization stemps, so I move objects between collections (if this is what is going on) without XSLT. Could someone help me on this?
I wouldn't advise "translating" an XSLT transformation into C# due to many reasons:
This isn't always easy due to the incompatibilities between the two languages:
Think how to simulate templates and match patterns in C# ?
How to simulate the XSLT processing model?
How to simulate importing stylesheet modules and determining which are the objects with highest import precedence?
How to simulate keys?
You will have to implement in C# a number of useful XPath (and XSLT) standard functions -- all the string functions, such as translate(), normalize-space(), substring-before(), substring-after(), ..., etc.
Instructions such as <xsl:number> and functions such as format-number().
So, if you have the time needed to invest in all this and the result is successful (which is not too-likely), the translation would be several times longer than the original and in most cases -- completely not-understandable, not extensible, not maintainable.
I also doubt that a translation would run significantly faster than the original -- it can run slower in some cases (for example, not implementing keys efficiently).
Conclusion: I strongly advise against engaging in such destructive activity.
You can do the XSTL transformation inside your C# program using the XslCompiledTransform class (reference : http://msdn.microsoft.com/en-us/library/system.xml.xsl.xslcompiledtransform.aspx).
Try to use Xsl To .NET Code Generator; it is an add-in to Microsoft Visual Studio 2010, it helps you generate C# code from an XSL template.
I tried chat.openai and got this conversion (I need to do more of this too):
using System;
using System.Linq;
using System.Xml.Linq;
namespace XsltToCSharp
{
class Program
{
static void Main(string[] args)
{
XNamespace t = "http://tempuri.org/";
XDocument input = XDocument.Load("input.xml");
XDocument output = new XDocument(
new XElement(t + "Data",
CopyFlagged(input, t),
CopyFlaggedDetails(input, t),
CopySystemRArray(input, t)
)
);
output.Save("output.xml");
}
static XElement CopyFlagged(XDocument input, XNamespace t)
{
return new XElement("Flagged",
input.Descendants(t + "Covers").Elements()
);
}
static XElement CopyFlaggedDetails(XDocument input, XNamespace t)
{
return new XElement("FlaggedDetails",
input.Descendants(t + "TotalFlaggedDetails").Elements()
);
}
static XElement CopySystemRArray(XDocument input, XNamespace t)
{
XElement rArray = new XElement("RArray");
var elems = input.Descendants(t + "Elem").ToList();
foreach (var elem in elems)
{
var code = (string)elem.Element(t + "Code");
var showAlways = (string)elem.Element(t + "ShowAlways");
var existingElem = input.Descendants(t + "Elem")
.Where(x => (string)x.Element(t + "Code") == code)
.FirstOrDefault();
if (existingElem == null)
{
rArray.Add(elem);
}
}
return rArray;
}
}
}
Here is another possibility using C# string literal.
using System;
using System.Linq;
using System.Xml.Linq;
namespace XsltToCSharp
{
class Program
{
static void Main(string[] args)
{
XNamespace t = "http://tempuri.org/";
XDocument input = XDocument.Load("input.xml");
XDocument output = XDocument.Parse(
$#"<Data xmlns=""http://tempuri.org/"">
{CopyFlagged(input, t)}
{CopyFlaggedDetails(input, t)}
{CopySystemRArray(input, t)}
</Data>"
);
output.Save("output.xml");
}
static string CopyFlagged(XDocument input, XNamespace t)
{
return $#"<Flagged>
{string.Join("", input.Descendants(t + "Covers").Elements().ToList().Select(x => x.ToString()))}
</Flagged>";
}
static string CopyFlaggedDetails(XDocument input, XNamespace t)
{
return $#"<FlaggedDetails>
{string.Join("", input.Descendants(t + "TotalFlaggedDetails").Elements().ToList().Select(x => x.ToString()))}
</FlaggedDetails>";
}
static string CopySystemRArray(XDocument input, XNamespace t)
{
string rArray = "";
var elems = input.Descendants(t + "Elem").ToList();
foreach (var elem in elems)
{
var code = (string)elem.Element(t + "Code");
var showAlways = (string)elem.Element(t + "ShowAlways");
var existingElem = input.Descendants(t + "Elem")
.Where(x => (string)x.Element(t + "Code") == code)
.FirstOrDefault();
if (existingElem == null)
{
rArray += elem.ToString();
}
}
return $#"<RArray>
{rArray}
</RArray>";
}
}
}
I have several xml files that are formated this way:
<ROOT>
<OBJECT>
<identity>
<id>123</id>
</identity>
<child2 attr = "aa">32</child2>
<child3>
<childOfChild3 att1="aaa" att2="bbb" att3="CCC">LN</childOfChild3>
</child3>
<child4>
<child5>
<child6>3ddf</child6>
<child7>
<childOfChild7 att31="RR">1231</childOfChild7>
</child7>
</child5>
</child4>
</OBJECT>
<OBJECT>
<identity>
<id>124</id>
</identity>
<child2 attr = "bb">212</child2>
<child3>
<childOfChild3 att1="ee" att2="ccc" att3="EREA">OP</childOfChild3>
</child3>
<child4>
<child5>
<child6>213r</child6>
<child7>
<childOfChild7 att31="EE">1233</childOfChild7>
</child7>
</child5>
</child4>
</OBJECT>
</ROOT>
How can i format it this way?:
<ROOT>
<OBJECT>
<id>123</id>
<child2>32</child2>
<attr>aa</attr>
<child3></child3>
<childOfChild3>LN</childOfChild3>
<att1>aaa</att1>
<att2>bbb</att2>
<att3>CCC</att3>
<child4></child4>
<child5></child5>
<child6>3ddf</child6>
<child7></child7>
<childOfChild7>1231</childOfChild7>
<att31>RR</att31>
</OBJECT>
<OBJECT>
<id>124</id>
<child2>212</child2>
<attr>bb</attr>
<child3></child3>
<childOfChild3>LN</childOfChild3>
<att1>ee</att1>
<att2>ccc</att2>
<att3>EREA</att3>
<child4></child4>
<child5></child5>
<child6>213r</child6>
<child7></child7>
<childOfChild7>1233</childOfChild7>
<att31>EE</att31>
</OBJECT>
</ROOT>
I know some C# so maybe a parser there? or some generic xslt?
The xml files are some data received from a client, so i can't control the way they are sending it to me.
L.E. Basically when i am trying to test this data in excel (for example i want to make sure that the attribute of childOfChild7 corresponds to the correct identity id) i am getting a lot of blank spaces. If i am importing in access to get only the data i want out, i have to do a thousands subqueries to get them all in a nice table. Basically i just want to see for one Object all its data (one object - One row) and then just delete/hide the columns i don't need.
Here is a pure XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="OBJECT//*[not(self::identity) and text()]">
<xsl:copy>
<xsl:apply-templates select="text()"/>
</xsl:copy>
<xsl:apply-templates select="#* | node()[not(self::text())]"/>
</xsl:template>
<xsl:template match="OBJECT//*[not(self::identity) and (not(text()))]">
<xsl:copy/>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="identity">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<ROOT>
<OBJECT>
<identity>
<id>123</id>
</identity>
<child2 attr = "aa">32</child2>
<child3>
<childOfChild3 att1="aaa" att2="bbb" att3="CCC">LN</childOfChild3>
</child3>
<child4>
<child5>
<child6>3ddf</child6>
<child7>
<childOfChild7 att31="RR">1231</childOfChild7>
</child7>
</child5>
</child4>
</OBJECT>
<OBJECT>
<identity>
<id>124</id>
</identity>
<child2 attr = "bb">212</child2>
<child3>
<childOfChild3 att1="ee" att2="ccc" att3="EREA">OP</childOfChild3>
</child3>
<child4>
<child5>
<child6>213r</child6>
<child7>
<childOfChild7 att31="EE">1233</childOfChild7>
</child7>
</child5>
</child4>
</OBJECT>
</ROOT>
the wanted, correct result is produced:
<ROOT>
<OBJECT>
<id>123</id>
<child2>32</child2>
<attr>aa</attr>
<child3/>
<childOfChild3>LN</childOfChild3>
<att1>aaa</att1>
<att2>bbb</att2>
<att3>CCC</att3>
<child4/>
<child5/>
<child6>3ddf</child6>
<child7/>
<childOfChild7>1231</childOfChild7>
<att31>RR</att31>
</OBJECT>
<OBJECT>
<id>124</id>
<child2>212</child2>
<attr>bb</attr>
<child3/>
<childOfChild3>OP</childOfChild3>
<att1>ee</att1>
<att2>ccc</att2>
<att3>EREA</att3>
<child4/>
<child5/>
<child6>213r</child6>
<child7/>
<childOfChild7>1233</childOfChild7>
<att31>EE</att31>
</OBJECT>
</ROOT>
You do it by flattening it out. You take all the descendants of the OBJECTS and turn them into elements. You should really try this for yourself instead of just accepting my code, but it works so you can test it vs what you come up with.
XElement root1 = XElement.Load(file1);
XElement root = new XElement("ROOT",
root1.Elements()
.Select(o => new XElement(o.Name, o
.Descendants()
.Select(x =>
{
List<XElement> list = new List<XElement>();
list.Add(new XElement(x.Name, x.HasElements ? "" : x.Value));
if (x.HasAttributes)
list.AddRange(x.Attributes()
.Select(a => new XElement(a.Name, a.Value))
);
return list;
})
))
.ToArray());
PS. You forgot <identity></identity> in your result set.
Just use XmlSerializer to deserialize it into a class, here is an msdn post on it, basically you build a class that matches your xml structure and then let the XmlSerializer class do the conversion, you shouldn't need to create your own parser
I am not familiar with C# but I am hoping this will atleast get you started. I had to do something similar, where I would get a XML file and parse the information into a database.
I used a Java lib ~ StAX. It will allow you read the XML file and parse the information into another XML file quite easily using the parent - child system. I hope this helped a little.