0

I need to use C# to manicure the response from a service before handing it off to the caller. The service takes a series of requests in XML format, formats the message and send it to the service. The response looks similar to this:

<SvcRes>
    <SvcVer>1.0</SvcVer>
    <MsgUUID>12345678-1234-1234-1234-123456789012</MsgUUID>
    <Svc>
        <SvcParms>
            <ApplID>App</ApplID>
            <SvcID>AppSrch</SvcID>
            <SvcVer>1.0</SvcVer>
        </SvcParms>
        <MsgData>
            <AppResData>
                <Key>xxxxxxxx</Key>
                <Rslt>xxxxxxxx</Rslt>
                <ErrCde>0</ErrCde>
                <ApplMsgLst>
                    <ApplMsg>
                        <ApplMsgApplId>D6</ApplMsgApplId>
                        <ApplMsgNbr>0</ApplMsgNbr>
                        <ApplMsgTxt>INQUIRY COMPLETE     09:23:53</ApplMsgTxt>
                        <ApplMsgErrInd>N</ApplMsgErrInd>
                    </ApplMsg>
                </ApplMsgLst>
            </AppResData>
        </MsgData>
        <ErrCde>0</ErrCde>
        <ErrMsg/>
    </Svc>
    <Svc>
        <SvcParms>
            <ApplID>DP</ApplID>
            <SvcID>DPKywrd</SvcID>
            <SvcVer>1.0</SvcVer>
            <RqstUUID>12345678-1234-1234-1234-123456789012</RqstUUID>
        </SvcParms>
        <MsgData>
            <AppResData>
                <Key>xxxxxxxx</Key>
                <Rslt>xxxxxxxx</Rslt>
                <ErrCde>0</ErrCde>
                <ApplMsgLst>
                    <ApplMsg>
                        <ApplMsgApplId>D6</ApplMsgApplId>
                        <ApplMsgNbr>0</ApplMsgNbr>
                        <ApplMsgTxt>INQUIRY COMPLETE     09:23:53</ApplMsgTxt>
                        <ApplMsgErrInd>N</ApplMsgErrInd>
                    </ApplMsg>
                </ApplMsgLst>
            </AppResData>
        </MsgData>
        <ErrCde>0</ErrCde>
        <ErrMsg/>
      <Svc>
    <ErrCde>0</ErrCde>
    <ErrMsg>Success</ErrMsg>
</SvcRes>

I need to pull out the AppResData nodes, then strip out the ApplMsgLst nodes from each of them before sending the results back to the caller. The resulting XML should be like this:

<AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
</AppResData>
<AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
</AppResData>

I can get a list of XElement nodes using the following 2 lines of code:

var xml = XElement.Parse(xmlResponse);
var msgData = xml.DescendantsAndSelf("MsgData");
  • then I can do string.Concat(msgData.Nodes()) to get the final string to return to the caller -- BUT at this point I don't know how to delete the inner ApplMsgLst nodes. I have tried converting it back to a string and reparsing, but of course it complains about multiple root elements. I have tried every Remove combination I can think of, but they always remove too much (everything) or nothing..

Is there another way to do this? There is no file, this is all string data in a SOAP web service.

DaveN59
  • 3,638
  • 8
  • 39
  • 51

2 Answers2

1

Here is an XSLT based solution.

Parameters:

  • Input XML as a string data type.
  • XSLT as a file.
  • Output XML as a string data type.

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="/SvcRes">
        <root>
            <xsl:for-each select="Svc/MsgData/AppResData">
                <xsl:copy>
                    <xsl:copy-of select="Key"/>
                    <xsl:copy-of select="Rslt"/>
                    <xsl:copy-of select="ErrCde"/>
                </xsl:copy>
            </xsl:for-each>
        </root>
    </xsl:template>
</xsl:stylesheet>

c#

void Main()
{
    const string XSLTFILE = @"e:\Temp\DaveN59.xslt";
    string inputXML = @"<SvcRes>
        ...
    </SvcRes>";

    try
    {
        StringReader rdr = new StringReader(inputXML);
        XPathDocument XPathDoc = new XPathDocument(rdr);

        XslCompiledTransform xslt = new XslCompiledTransform();
        xslt.Load(XSLTFILE);

        StringWriter sw = new StringWriter();
        using (XmlWriter xwo = XmlWriter.Create(sw, xslt.OutputSettings))
        {
            xslt.Transform(XPathDoc, xwo);
        }
        
        Console.WriteLine(sw.ToString());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

Output XML

<root>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
</root>
Yitzhak Khabinsky
  • 18,471
  • 2
  • 15
  • 21
  • So basically just copy the good stuff to a new string / XML construct and then convert that to a new output string. I had to think about that for a bit, but since I am already pulling the "input" XML from a larger response message (a detail not mentioned in my post), this approach should make it easy to do both at the same time, instead of running 2 different transforms. I like it, I'll try it! – DaveN59 Aug 01 '21 at 14:43
  • This works perfectly for what I needed, thanks! I did add an initial step of pre-compiling the xslt file rather than load the file for every iteration or even every instance. We could easily have hundreds of entries in the XML input, and dozens, if not hundreds, of simultaneous calls. Other than that, this fit the bill perfectly – DaveN59 Aug 02 '21 at 14:37
0

You can try this with Cinchoo ETL - an open source library to do this transformation easily

StringBuilder xml = new StringBuilder();

using (var r = ChoXmlReader.LoadText("*** YOUR XML CONTENTS ***")
    .WithXPath("//AppResData")
    .WithField("Key")
    .WithField("Rslt")
    .WithField("ErrCde")
    )
{
    using (var w = new ChoXmlWriter(xml)
        )
    {
        w.Write(r);
    }
}
Console.WriteLine(xml.ToString());

Output:

<Root>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
</Root>
Cinchoo
  • 6,088
  • 2
  • 19
  • 34
  • Although this library sounds interesting and useful, I don't have time to prove it out prior to going to production so I went with the more "bare metal " approach from Vitzhak – DaveN59 Aug 02 '21 at 14:32