Update xml file by calling a C# function in xslt

2020-05-01 23:39发布

问题:

I have a xml file and an xslt file in my website project.

xml file:

<root>
  <employee>
    <firstname>Kaushal</firstname>
    <lastname>Parik</lastname>
  </employee>
  <employee>
    <firstname>Abhishek</firstname>
    <lastname>Swarnkar</lastname>
  </employee>
</root>

xslt:

<?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" xmlns:Concat="urn:XslSample">
  <xsl:output method="html" indent="yes"/>
  <xsl:template match="root">
      <xsl:for-each select="employee">
        <![CDATA[Concatenated name is ]]>
            <xsl:value-of select="Concat:GetFullName(firstname,lastname)"/>
            <xsl:value-of select="age"/>
        <br />
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

aspx.cs:

using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml;
using System.IO;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            MemoryStream objStream = new MemoryStream();
            StreamWriter objWriter = new StreamWriter(objStream, System.Text.Encoding.UTF8);

            XPathDocument doc = new XPathDocument(Server.MapPath("XMLFile.xml"));
            XslCompiledTransform trans = new XslCompiledTransform();
            trans.Load(Server.MapPath("XSLTFile.xslt"));

            //create the XslArgumentList and new BookUtils object
            XsltArgumentList argList = new XsltArgumentList();

            Concat objUtil = new Concat();

            //this tells the argumentlist about BookUtils
            argList.AddExtensionObject("urn:XslSample", objUtil);

            //new XPathNavigator
            XPathNavigator nav = doc.CreateNavigator();

            //do the transform
            trans.Transform(nav, argList, objWriter);
            //objWriter.Flush();
            objStream.Position = 0;
            StreamReader oReader = new StreamReader(objStream);
            string strResult = oReader.ReadToEnd();
            //objWriter.Close();
            //oReader.Close();
            Response.Write(strResult);
        }
        catch (Exception Ex)
        { Response.Write(Ex.Message); }
    }
}

public class Concat
{
    public Concat()
    { }

    public string GetFullName(string firstname, string lastname)
    { return "Mr." + firstname; }
}

When I run the site, I need to call a c# function from xslt and alter the values in xml file.... I am adding a text (say "Mr.") in front of every firstname through a c# code.... After adding, it writes the output as a response, but the original xml is not modified. I want that to be reflected in the xml file....

xml output needed:

<root>
  <employee>
    <firstname>Mr.Kaushal</firstname>
    <lastname>Parik</lastname>
  </employee>
  <employee>
    <firstname>Mr.Abhishek</firstname>
    <lastname>Swarnkar</lastname>
  </employee>
</root>

Also, as a next step, I need to add another node in the xml file (say age) through another c# function.... Please note that the c# function should be called from my xslt file.... Can anyone help me with a simple code for this????

Final xml required:

<root>
  <employee>
    <firstname>Mr.Kaushal</firstname>
    <lastname>Parik</lastname>
    <age>34</age>
  </employee>
  <employee>
    <firstname>Mr.Abhishek</firstname>
    <lastname>Swarnkar</lastname>
    <age>30</age>
  </employee>
</root>

回答1:

You could consider using the XmlDocument class here;

For example, you actually have the XML you want in the stream output from your XslTransform:

XmlDocument resultDocument = new XmlDocument();
resultDocument.Load(objStream);
resultDocument.Save(Server.MapPath("XMLFile.xml"));

EDIT. You'll also need to change your XSLT for this to work. Something like:

<?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" xmlns:Concat="urn:XslSample">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">
        <root>
            <xsl:apply-templates select="/root/employee" />
        </root>

  </xsl:template>

    <xsl:template match="employee">
        <employee>
            <firstname>Mr. <xsl:value-of select="./firstname"/></firstname>
            <lastname><xsl:value-of select="./lastname"/></lastname>
            <age>24</age>
        </employee>
    </xsl:template>

</xsl:stylesheet>

Should work. However, this is probably not the best approach as you need to supply an age per person and you don't seem to be sourcing this from anywhere. I'd recommend the DOM approach.

You also have to be careful here, though; it's conceivable that there might be a read lock on the file if it's being read by another process. If this is a one off exercise I'd consider just doing this prior to deploying your website.

Another alternative is to not use XSLT. In this instance you could also consider simple DOM processing:

XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("XMLFile.xml"));

XmlNodeList employeeNodes = doc.SelectNodes("//employee");

foreach(XmlNode employeeNode in employeeNodes)
{
    employeeNode.SelectSingleNode("./firstname").InnerText = String.Format("Mr. {0}", employeeNode.SelectSingleNode("./firstname").InnerText);
    XmlNode ageNode = doc.CreateElement("age");
    ageNode.InnerText = "3 Billion Years";
    employeeNode.ChildNodes.Add(ageNode0;
}

doc.Save();

I haven't compile checked this code but hopefully the intent is clear. Note that you could also use XDocument if you preferred.



回答2:

Please refer answer of this question for first step of question.

For next step of answer, use following xsl:

<?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"
            xmlns:myUtils="pda:MyUtils">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="vQ">Mr. </xsl:variable>
<xsl:template match="@*|node()">
<xsl:copy>
  <xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="employee/firstname">
<xsl:element name="firstname">
  <xsl:value-of select="myUtils:FormatName(.)" />
</xsl:element>
<xsl:element name="Age">
  <xsl:value-of select="myUtils:AddAge()" />
</xsl:element>
</xsl:template>

<xsl:template match="employee/firstname">
<xsl:element name="firstname">
  <xsl:value-of select="myUtils:FormatName(.)" />
</xsl:element>
</xsl:template>

<xsl:template match="employee">
<xsl:element  name="employee">
  <xsl:apply-templates select="@* | *"/>
  <xsl:element name="Age">
    <xsl:value-of select="myUtils:AddAge()" />
  </xsl:element>
</xsl:element>
</xsl:template>

</xsl:stylesheet>  

And add following function in MyXslExtension class:

public string AddAge()
    {
        return "25";
    }  

You will get the output like this:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="cdcatalog.xsl"?>
<root>
<employee>
<firstname>Mr. Kaushal</firstname>
<lastname>Parik</lastname>
<Age>25</Age>
</employee>
<employee>
<firstname>Mr. bhishek</firstname>
<lastname>Swarnkar</lastname>
<Age>25</Age>
</employee>
</root>  

Hope this will help you....