Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting quotes in innerXML to go on the wire as "

Tags:

c#

.net

wcf

(warning - unholy mixture of xml and gratuitous character encoding below.)

Short version: Why can't I get my service reference call (c#, .net 3.5, automagic service reference code added to VS2008) to properly encode a parameter that's supposed to look like this one the wire: (look for the " bits...those are my bane.)

(other extra soap-y bits removed for clarity)

<SOAP-ENV:Body><SOAPSDK4:SetCondition xmlns:SOAPSDK4="http://tempuri.org/message/">
<sharedSecret>buggerall</sharedSecret>

<xmlData>&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO action=&quot;add&quot; status=&
quot;3&quot; name=&quot;TestProfile2&quot; mask=&quot;0&quot; campaign_id=&quot;33&quot; 
campaign_protected=&quot;N&quot; condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/&
gt;&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;</xmlData>
</SOAPSDK4:SetCondition></SOAP-ENV:Body>   

I set the parameter, make the call...and the service returns a nice message saying "NO SOU-- SOAP FOR YOU!"

I tried several other formats in my passed-to-the-webservice parameter string:

action=\"add\"

which gave me this on the wire (via fiddler): action="add"

action=&quot;add&quot;

which gave me this on the wire: action=&amp;quot;add&amp;quot;

and various combinations (action=""add"" ?! ) with html.encode, url.encode which pretty much either completely bombed, or showed as double quotes on the wire.

Oh, and I even tried <![CDATA[&quot;]] surrounding. That didn't work either.

Is there any way to force a double quote encoding in the innerHtml bit of the soap message?

*(because that's how the service wants them. don't ask questions. these aren't the droids you're looking for)

* * * Long, tortuous version:

I'm writing an app to automate some procedures that are currently handled by a (winform) administrative GUI app. (Actually, it's an mmc snap in. Anyway.)

To accomplish it's tasks, the winform app communicates with it's server via standard web service calls.

I'm using VS2008's nifty "web service reference" auto generation thingie (that's the technical description), and I've successfully authenticated to the web service. To make sure I was doing things correctly, I captured the calls from the GUI app, and then compared them to what I was sending out on the wire. All was good. Then I ran into the evils of the ampersand. (more properly, how to get things to encode properly)

For one of the calls, the web service expects to see something like this: (I captured the app sending this via fiddler)

<?xml version="1.0" encoding="UTF-8" standalone="no"?><SOAP-ENV:Envelope
xmlns:SOAPSDK1="http://www.w3.org/2001/XMLSchema" xmlns:SOAPSDK2="
http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAPSDK3="
http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="
http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAPSDK4:SetCondition
xmlns:SOAPSDK4="http://tempuri.org/message/"><sharedSecret>0500001007C3525F3-F315-460D-
AF5C-D84767130126094</sharedSecret><xmlData>&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO  
action=&quot;add&quot; status=&quot;3&quot; name=&quot;TestProfile2&quot; mask=&
quot;0&quot; campaign_id=&quot;33&quot;campaign_protected=&quot;N&quot;
condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;&
lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;</xmlData></SOAPSDK4:SetCondition></SOAP-
ENV:Body></SOAP-ENV:Envelope>

Stripping out all the extra SOAP-y stuff to show the relevant bit - this is the <xmlData> section that's passed across. Notice the &quot; surrounding the parameters:

&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO action=&quot;add&quot;
status=&quot;3&quot; name=&quot;TestProfile2&quot; mask=&quot;0&quot;
campaign_id=&quot;33&quot; campaign_protected=&quot;N&quot;
condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;&
lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;

In my code, I have a string built up like so:

var serviceParams = "<SEARCHINFO-LIST><SEARCH_INFO action=\"add\"
status=\"3\" name=\"TestProfileFromExternApp\" mask=\"0\" campaign_id=\"33\"
campaign_protected=\"N\"
condition_protected=\"N\"><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>";

When my app sends it out over the wire, fiddler captures this: (again, stripping out all the SOAP stuff)

&lt;SEARCHINFO-LIST&gt;&lt;SEARCH_INFO action="add" status="3"
name="TestProfileFromExternApp" mask="0" campaign_id="33"
campaign_protected="N" condition_protected="N"&gt;&lt;CONDITIONS/&gt;
&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;

And the receiving service sends back an error that it doesn't like it. It wants the &quot; d##nit.

The angle brackets get properly encoded, but the quotes are valid in the HTTP string, and do not get encoded.

"Ah-ha!" says I, "I'll just pre-manually encode things!". I attempted to do something like this:

var serviceParams = "<SEARCHINFO-LIST><SEARCH_INFO action=&quot;add&quot;
status=&quot;3&quot; name=&quot;TestProfileFromExternApp&quot;
mask=&quot;0&quot; campaign_id=&quot;33&quot;
campaign_protected=&quot;N&quot;
condition_protected=&quot;N&quot;><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>";

Which was sent out as (again, via fiddler) and all my ampersands (in the &quot;) get converted to &amp;quot; like so:

&lt;SEARCHINFO-LIST&gt;&lt;SEARCH_INFO action=&amp;quot;add&amp;quot;
status=&amp;quot;3&amp;quot;
name=&amp;quot;TestProfileFromExternApp&amp;quot; mask=&amp;quot;0&amp;quot;
campaign_id=&amp;quot;33&amp;quot; campaign_protected=&amp;quot;N&amp;quot;
condition_protected=&amp;quot;N&amp;quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;

And, as you can guess, the receiving web service came back with "BZZT! Thank you for playing!".

I've tried all sorts of escape and encode sequences with similar results. Effectively, after all my manipulation it goes through something like HttpUtility.HtmlEncode right before going out on the wire, and any ampersands in the string get converted to &amp;. And quotes (single or double) are ignored in the conversion. And the receiving web service wants those quotes represented as &quot; doggone it, or it's going to take it's ball and go home.

My last, desperate hope was to catch the message right (I thought) before it went onto the wire using IClientMessageInspector to implement message inspection in the BeforeSendRequest event...and manually set those things before it went to the wire.

I capture the message fine. I can even manually put in &quot;.

But when it gets sent, both wireshark and fiddler assure me it's going out nicely formatted...with the quotes I'm so desperately trying to get rid of.

<xmlData xsi:type="xsd:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
&lt;SEARCHINFO-LIST&gt;&lt;SEARCH_INFO action="add" status="3" 
name="TestProfileFromExternApp" mask="0" campaign_id="33" campaign_protected="N" 
condition_protected="N"&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;
&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;</xmlData>

I'm at my wits end. I'll accept any suggestions up to and including sacrificing [small cuddly thing] on the alter of [vile deity] or selling same my [soul/heart/son's bionacle collection]. Trust me, it'd be the lesser evil.

Per request below, here's the generated message stub: (I think this is what you were asking for...)

public int SetCondition(string sharedSecret, string xmlData, out string resultValue) 
{
    tzGui.tzCampaign.SetConditionRequest inValue = new tzGui.tzCampaign.SetConditionRequest();
    inValue.sharedSecret = sharedSecret;
    inValue.xmlData = xmlData;
    tzGui.tzCampaign.SetConditionResponse retVal = ((tzGui.tzCampaign.CampaignSoapPort)(this)).SetCondition(inValue);
    resultValue = retVal.resultValue;
    return retVal.Result;
}

And here's how it's getting called:

void SetConditionTask()
{
    //ok, now we *try* and create a new profile
    var tzCampaignCxn = new tzCampaign.CampaignSoapPortClient("CampaignSoapBinding");
    //no worky
    //string xmlData = "<SEARCHINFO-LIST><SEARCH_INFO action=\"add\" status=\"3\" name=\"TestProfileFromExternApp\" mask=\"0\" campaign_id=\"33\" campaign_protected=\"N\" condition_protected=\"N\"><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>";

    //this one doesn't work
    //string xmlData = "<SEARCHINFO-LIST><SEARCH_INFO action=<![CDATA[ &quot; ]]>add<![CDATA[ &quot; ]]> status=<![CDATA[ &quot; ]]>3<![CDATA[ &quot; ]]> name=<![CDATA[ &quot; ]]>TestProfileFromExternApp<![CDATA[ &quot; ]]> mask=<![CDATA[ &quot; ]]>0<![CDATA[ &quot; ]]> campaign_id=<![CDATA[ &quot; ]]>33<![CDATA[ &quot; ]]> campaign_protected=<![CDATA[ &quot; ]]>N<![CDATA[ &quot; ]]> condition_protected=<![CDATA[ &quot; ]]>N<![CDATA[ &quot; ]]>><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>";

    //this one doesn't either
    string xmlData = "<SEARCHINFO-LIST><SEARCH_INFO action=&quot;add&quot; status=&quot;3&quot; name=&quot;TestProfileFromExternApp&quot; mask=&quot;0&quot; campaign_id=&quot;33&quot; campaign_protected=&quot;N&quot; condition_protected=&quot;N&quot;><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>";

    string createProfileResultVal = string.Empty;
    tzCampaignCxn.SetCondition(SharedSecret, xmlData, out createProfileResultVal);
    txtResults.AppendText(Environment.NewLine + Environment.NewLine + createProfileResultVal);
    }
like image 824
Aaron B. Avatar asked Nov 05 '22 17:11

Aaron B.


1 Answers

If I understand your long post, you seem to be building parts of your XML by using strings. Don't do that. Always use one of the XML APIs to create XML. It knows the quoting rules.

like image 121
John Saunders Avatar answered Nov 14 '22 00:11

John Saunders