在innerXML中获取引号以作为“

时间:2010-01-25 23:38:58

标签: c# .net wcf

(警告 - 下面的xml和无偿字符编码的混合混合。)

简短版本 为什么我不能得到我的服务引用调用(c#,。net 3.5,添加到VS2008的自动服务引用代码)来正确编码一个看起来像这样的参数的参数:(查找"位......那些是我的祸根。)

(为清楚起见,删除了其他额外的肥皂碎片)

<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>   

我设置参数,拨打电话......服务返回一条很好的消息,上面写着“没有SOU--肥皂给你!”

我在传递给webservice参数字符串中尝试了其他几种格式:

action=\"add\"

在线上给我这个(通过提琴手):action="add"

action=&quot;add&quot;

在电汇上给了我这个:action=&amp;quot;add&amp;quot;

和各种组合(action =“”add“”?​​!)与html.encode,url.encode几乎完全被轰炸,或在电线上显示为双引号。

哦,我甚至试过<![CDATA[&quot;]]周围。这也不起作用。

有没有办法在soap消息的innerHtml位强制使用双引号编码?

*(因为这就是服务所需要的。不要问问题。这些不是您正在寻找的机器人)

* * * 冗长曲折的版本:

我正在编写一个应用程序来自动执行当前处理的某些过程 一个(winform)管理GUI应用程序。 (实际上,它是一个mmc snap in。 反正。)

为了完成它的任务,winform应用程序通过它与服务器进行通信 标准的网络服务电话。

我正在使用VS2008的漂亮“网络服务参考”自动生成东西 (这是技术说明),我已成功通过身份验证 网络服务。为了确保我正确地做事,我抓住了 从GUI应用程序调用,然后将它们与我发送的内容进行比较 电线。一切都很好。然后我遇到了&符号的邪恶。 (更多 正确的,如何正确编码的事情)

对于其中一个调用,Web服务期望看到如下内容: (我抓住了通过提琴手发送的应用程序)

<?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>

删除所有额外的SOAP-y内容以显示相关位 - 这是传递的<xmlData>部分。请注意参数周围的&quot;

&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;

在我的代码中,我有一个像这样构建的字符串:

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>";

当我的应用程序通过网络将其发送出去时,fiddler会抓住这个:(再次, 剥离所有SOAP的东西)

&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;

接收服务发回一个它不喜欢它的错误。它需要&quot; d ## nit。

尖括号被正确编码,但引号在 HTTP字符串,不要编码。

“啊,哈!”我说,“我只是预先手动编码!”。我试图 做这样的事情:

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>";

这是作为(再次,通过提琴手)和我所有的&符号(在 &quot;}转换为&amp;quot;,如此:

&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;

而且,正如您所猜测的那样,接收网络服务带回“BZZT! 谢谢你的演奏!“。

我尝试了各种各样的转义和编码序列,结果相似。 实际上,在我完成所有操作后,它会经历类似的事情 在出线之前HttpUtility.HtmlEncode 正确,任何 字符串中的&符号将转换为&amp;。和报价(单或 double)在转换中被忽略。和接收Web服务 想要那些表示为&quot;的引用,或者它会发生 拿起球回家。

我的最后一个绝望的希望是在使用事件中的IClientMessageInspector to implement message inspection进入线路之前抓住正确的信息(我认为)...并且手动设置这些信息在它进入电线之前。

我捕获邮件很好。我甚至可以手动输入&quot;

但是当它被发送时,wireshark和小提琴手都向我保证它的格式很好......用引号我非常想要摆脱它。

<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>

我的智慧结束了。我会接受任何建议,包括牺牲 [小可爱的东西] 来改变 [vile deity] 或出售同样的 [灵魂/心灵/儿子的bionacle系列] 。相信我,这是较小的邪恶。

下面的每个请求,这是生成的消息存根: (我认为这就是你要求的......)

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;
}

以下是它的调用方式:

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);
    }

3 个答案:

答案 0 :(得分:2)

如果我理解你的长篇文章,你似乎是通过使用字符串来构建XML的一部分。不要那样做。始终使用其中一个XML API来创建XML。它知道引用规则。

答案 1 :(得分:0)

您需要将该字符串作为CDATA部分放入SOAP消息中,以便您可以根据需要对其进行格式化,以便WCF不会触及它。

你目前遇到的问题是该服务期望在xml和html之间的编码 - 因此Soap格式化程序使用的格式不能很好地处理 - 所以你总是要逃避引用自己。

我预感到,如果你将它包围在你的字符串参数中的CDATA标记<![CDATA[*yourstring*]]>中,那么它的标记将通过网络转义并以&lt;![[*yourstring-XML-element-encoded*]]&gt;的形式到达另一端

如果是这种情况,您可以做的是更改由VS服务引用生成器自动生成的代码,以便代理上的方法调用的参数类型接受基本上的类型与字符串相同,但将自身序列化为CDATA部分。这种类型是基于Marc Gravell编写的代码来回答another question here。这样做的问题是,如果有人在VS中对该服务引用使用“更新引用”命令,那么您所做的任何更改都将丢失。

所以,相反,使用svcutil命令行实用程序生成引用(与流行的看法相反,VS不使用此工具 - 这是一种耻辱,因为它更灵活),将它生成的配置和代码导入到您的手动项目并摆脱'服务参考'。这样,代码就是你自己想要的,你可以在VS的项目树中轻松看到它。

答案 2 :(得分:0)

我从来没有完全弄明白如何让它发挥作用。但是,我确实找到了解决方案。一个丑陋,黑客的解决方案,我并不为此感到骄傲。但它奏效了。

为了后代的利益,这就是我最终做的事情。对于我需要进行的每个(10)调用,我只是通过fiddler捕获SOAP调用,然后编写一个快速搜索n替换例程

    public string Newrule(string ruleName, DecisionSet decisionSet)
    {
        var soapString = "<?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></sharedSecret>" +
                         "<xmlData>&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO action=&quot;add&quot; status=&quot;3&quot; " +
                         "name=&quot;" + ruleName + "&quot; mask=&quot;0&quot; DecisionSet_id=&quot;" + decisionSet.Id +
                         "&quot; DecisionSet_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>";

        var headerUrl = "http://tempuri.org/action/DecisionSet.SetCondition";
        var serviceUrl = "/webservice/DecisionSet.WSDL";
        var result = sender.MakeRequest(soapString, serviceUrl, headerUrl,null);
        var idSearch = @"SEARCH_INFO id=&quot;(\d+)&quot;";

        var ruleId = Regex.Match(result, idSearch).Groups[1].Value;

        return ruleId;
    }

这称为使用适当的标头进行http调用的简单例程。 不太优雅,但它有效。

    public string MakeRequest(string requestString, string serviceUrl, string headerUrl, string useragent)
    {
        string query = requestString.Replace(@"<sharedSecret></sharedSecret>", "<sharedSecret>"+secret+"</sharedSecret>");
        query = query.Replace(@"<SessionID></SessionID>", "<SessionID>" + secret + "</SessionID>");
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(server + serviceUrl);
        //if (proxy != null) req.Proxy = new WebProxy(proxy, true);
        req.Headers.Add("SOAPAction", headerUrl);
        if (useragent == null)
            req.UserAgent = "SOAP Toolkit 3.0";
        else
        {
            req.UserAgent = useragent;
        }
        req.ContentType = "text/xml;charset=\"utf-8\"";
        req.Accept = "text/xml";
        req.Method = "POST";
        Stream stm = req.GetRequestStream();

        StreamWriter sw = new StreamWriter(stm);
        sw.Write(query);
        sw.Flush();
        stm.Close();
        WebResponse resp = req.GetResponse();
        stm = resp.GetResponseStream();
        StreamReader r = new StreamReader(stm);
        string response = (r.ReadToEnd());

        return response;
    }