Alternative to HttpUtility for .NET 3.5 SP1 client

2019-02-04 16:51发布

It'd be really nice to target my Windows Forms app to the .NET 3.5 SP1 client framework. But, right now I'm using the HttpUtility.HtmlDecode and HttpUtility.UrlDecode functions, and the MSDN documentation doesn't point to any alternatives inside of, say, System.Net or something.

So, short from reflectoring the source code and copying it into my assembly---which I don't think would be worth it---are there alternatives inside of the .NET 3.5 SP1 client framework that you know of, to replace this functionality? It seems a bit strange that they'd restrict these useful functions to server-only code.

7条回答
Bombasti
2楼-- · 2019-02-04 17:28

I reverse engineered the Microsoft System.Net.WebUtility class from .NET 4.0 using Reflector (I think they'd be ok with it given the circumstances). So you could either use .NET 4.0 Client Framework (which now has this new class) or use the code here.

As long as you use a strong name on your assembly etc., you'll be safe enough. Here:

/// <summary>
///     Taken from System.Net in 4.0, useful until we move to .NET 4.0 - needed for Client Profile
/// </summary>
public static class WebUtility
{
    // Fields
    private static char[] _htmlEntityEndingChars = new char[] { ';', '&' };

    // Methods
    public static string HtmlDecode(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return value;
        }
        if (value.IndexOf('&') < 0)
        {
            return value;
        }
        StringWriter output = new StringWriter(CultureInfo.InvariantCulture);
        HtmlDecode(value, output);
        return output.ToString();
    }

    public static void HtmlDecode(string value, TextWriter output)
    {
        if (value != null)
        {
            if (output == null)
            {
                throw new ArgumentNullException("output");
            }
            if (value.IndexOf('&') < 0)
            {
                output.Write(value);
            }
            else
            {
                int length = value.Length;
                for (int i = 0; i < length; i++)
                {
                    char ch = value[i];
                    if (ch == '&')
                    {
                        int num3 = value.IndexOfAny(_htmlEntityEndingChars, i + 1);
                        if ((num3 > 0) && (value[num3] == ';'))
                        {
                            string entity = value.Substring(i + 1, (num3 - i) - 1);
                            if ((entity.Length > 1) && (entity[0] == '#'))
                            {
                                ushort num4;
                                if ((entity[1] == 'x') || (entity[1] == 'X'))
                                {
                                    ushort.TryParse(entity.Substring(2), NumberStyles.AllowHexSpecifier, (IFormatProvider)NumberFormatInfo.InvariantInfo, out num4);
                                }
                                else
                                {
                                    ushort.TryParse(entity.Substring(1), NumberStyles.Integer, (IFormatProvider)NumberFormatInfo.InvariantInfo, out num4);
                                }
                                if (num4 != 0)
                                {
                                    ch = (char)num4;
                                    i = num3;
                                }
                            }
                            else
                            {
                                i = num3;
                                char ch2 = HtmlEntities.Lookup(entity);
                                if (ch2 != '\0')
                                {
                                    ch = ch2;
                                }
                                else
                                {
                                    output.Write('&');
                                    output.Write(entity);
                                    output.Write(';');
                                    goto Label_0117;
                                }
                            }
                        }
                    }
                    output.Write(ch);
                Label_0117: ;
                }
            }
        }
    }

    public static string HtmlEncode(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return value;
        }
        if (IndexOfHtmlEncodingChars(value, 0) == -1)
        {
            return value;
        }
        StringWriter output = new StringWriter(CultureInfo.InvariantCulture);
        HtmlEncode(value, output);
        return output.ToString();
    }

    public static unsafe void HtmlEncode(string value, TextWriter output)
    {
        if (value != null)
        {
            if (output == null)
            {
                throw new ArgumentNullException("output");
            }
            int num = IndexOfHtmlEncodingChars(value, 0);
            if (num == -1)
            {
                output.Write(value);
            }
            else
            {
                int num2 = value.Length - num;
                fixed (char* str = value)
                {
                    char* chPtr = str;
                    char* chPtr2 = chPtr;
                    while (num-- > 0)
                    {
                        chPtr2++;
                        output.Write(chPtr2[0]);
                    }
                    while (num2-- > 0)
                    {
                        chPtr2++;
                        char ch = chPtr2[0];
                        if (ch <= '>')
                        {
                            switch (ch)
                            {
                                case '&':
                                    {
                                        output.Write("&amp;");
                                        continue;
                                    }
                                case '\'':
                                    {
                                        output.Write("&#39;");
                                        continue;
                                    }
                                case '"':
                                    {
                                        output.Write("&quot;");
                                        continue;
                                    }
                                case '<':
                                    {
                                        output.Write("&lt;");
                                        continue;
                                    }
                                case '>':
                                    {
                                        output.Write("&gt;");
                                        continue;
                                    }
                            }
                            output.Write(ch);
                            continue;
                        }
                        if ((ch >= '\x00a0') && (ch < 'Ā'))
                        {
                            output.Write("&#");
                            output.Write(((int)ch).ToString(NumberFormatInfo.InvariantInfo));
                            output.Write(';');
                        }
                        else
                        {
                            output.Write(ch);
                        }
                    }
                }
            }
        }
    }

    private static unsafe int IndexOfHtmlEncodingChars(string s, int startPos)
    {
        int num = s.Length - startPos;
        fixed (char* str = s)
        {
            char* chPtr = str;
            char* chPtr2 = chPtr + startPos;
            while (num > 0)
            {
                char ch = chPtr2[0];
                if (ch <= '>')
                {
                    switch (ch)
                    {
                        case '&':
                        case '\'':
                        case '"':
                        case '<':
                        case '>':
                            return (s.Length - num);

                        case '=':
                            goto Label_0086;
                    }
                }
                else if ((ch >= '\x00a0') && (ch < 'Ā'))
                {
                    return (s.Length - num);
                }
            Label_0086:
                chPtr2++;
                num--;
            }
        }
        return -1;
    }

    // Nested Types
    private static class HtmlEntities
    {
        // Fields
        private static string[] _entitiesList = new string[] { 
        "\"-quot", "&-amp", "'-apos", "<-lt", ">-gt", "\x00a0-nbsp", "\x00a1-iexcl", "\x00a2-cent", "\x00a3-pound", "\x00a4-curren", "\x00a5-yen", "\x00a6-brvbar", "\x00a7-sect", "\x00a8-uml", "\x00a9-copy", "\x00aa-ordf", 
        "\x00ab-laquo", "\x00ac-not", "\x00ad-shy", "\x00ae-reg", "\x00af-macr", "\x00b0-deg", "\x00b1-plusmn", "\x00b2-sup2", "\x00b3-sup3", "\x00b4-acute", "\x00b5-micro", "\x00b6-para", "\x00b7-middot", "\x00b8-cedil", "\x00b9-sup1", "\x00ba-ordm", 
        "\x00bb-raquo", "\x00bc-frac14", "\x00bd-frac12", "\x00be-frac34", "\x00bf-iquest", "\x00c0-Agrave", "\x00c1-Aacute", "\x00c2-Acirc", "\x00c3-Atilde", "\x00c4-Auml", "\x00c5-Aring", "\x00c6-AElig", "\x00c7-Ccedil", "\x00c8-Egrave", "\x00c9-Eacute", "\x00ca-Ecirc", 
        "\x00cb-Euml", "\x00cc-Igrave", "\x00cd-Iacute", "\x00ce-Icirc", "\x00cf-Iuml", "\x00d0-ETH", "\x00d1-Ntilde", "\x00d2-Ograve", "\x00d3-Oacute", "\x00d4-Ocirc", "\x00d5-Otilde", "\x00d6-Ouml", "\x00d7-times", "\x00d8-Oslash", "\x00d9-Ugrave", "\x00da-Uacute", 
        "\x00db-Ucirc", "\x00dc-Uuml", "\x00dd-Yacute", "\x00de-THORN", "\x00df-szlig", "\x00e0-agrave", "\x00e1-aacute", "\x00e2-acirc", "\x00e3-atilde", "\x00e4-auml", "\x00e5-aring", "\x00e6-aelig", "\x00e7-ccedil", "\x00e8-egrave", "\x00e9-eacute", "\x00ea-ecirc", 
        "\x00eb-euml", "\x00ec-igrave", "\x00ed-iacute", "\x00ee-icirc", "\x00ef-iuml", "\x00f0-eth", "\x00f1-ntilde", "\x00f2-ograve", "\x00f3-oacute", "\x00f4-ocirc", "\x00f5-otilde", "\x00f6-ouml", "\x00f7-divide", "\x00f8-oslash", "\x00f9-ugrave", "\x00fa-uacute", 
        "\x00fb-ucirc", "\x00fc-uuml", "\x00fd-yacute", "\x00fe-thorn", "\x00ff-yuml", "Œ-OElig", "œ-oelig", "Š-Scaron", "š-scaron", "Ÿ-Yuml", "ƒ-fnof", "ˆ-circ", "˜-tilde", "Α-Alpha", "Β-Beta", "Γ-Gamma", 
        "Δ-Delta", "Ε-Epsilon", "Ζ-Zeta", "Η-Eta", "Θ-Theta", "Ι-Iota", "Κ-Kappa", "Λ-Lambda", "Μ-Mu", "Ν-Nu", "Ξ-Xi", "Ο-Omicron", "Π-Pi", "Ρ-Rho", "Σ-Sigma", "Τ-Tau", 
        "Υ-Upsilon", "Φ-Phi", "Χ-Chi", "Ψ-Psi", "Ω-Omega", "α-alpha", "β-beta", "γ-gamma", "δ-delta", "ε-epsilon", "ζ-zeta", "η-eta", "θ-theta", "ι-iota", "κ-kappa", "λ-lambda", 
        "μ-mu", "ν-nu", "ξ-xi", "ο-omicron", "π-pi", "ρ-rho", "ς-sigmaf", "σ-sigma", "τ-tau", "υ-upsilon", "φ-phi", "χ-chi", "ψ-psi", "ω-omega", "ϑ-thetasym", "ϒ-upsih", 
        "ϖ-piv", " -ensp", " -emsp", " -thinsp", "‌-zwnj", "‍-zwj", "‎-lrm", "‏-rlm", "–-ndash", "—-mdash", "‘-lsquo", "’-rsquo", "‚-sbquo", "“-ldquo", "”-rdquo", "„-bdquo", 
        "†-dagger", "‡-Dagger", "•-bull", "…-hellip", "‰-permil", "′-prime", "″-Prime", "‹-lsaquo", "›-rsaquo", "‾-oline", "⁄-frasl", "€-euro", "ℑ-image", "℘-weierp", "ℜ-real", "™-trade", 
        "ℵ-alefsym", "←-larr", "↑-uarr", "→-rarr", "↓-darr", "↔-harr", "↵-crarr", "⇐-lArr", "⇑-uArr", "⇒-rArr", "⇓-dArr", "⇔-hArr", "∀-forall", "∂-part", "∃-exist", "∅-empty", 
        "∇-nabla", "∈-isin", "∉-notin", "∋-ni", "∏-prod", "∑-sum", "−-minus", "∗-lowast", "√-radic", "∝-prop", "∞-infin", "∠-ang", "∧-and", "∨-or", "∩-cap", "∪-cup", 
        "∫-int", "∴-there4", "∼-sim", "≅-cong", "≈-asymp", "≠-ne", "≡-equiv", "≤-le", "≥-ge", "⊂-sub", "⊃-sup", "⊄-nsub", "⊆-sube", "⊇-supe", "⊕-oplus", "⊗-otimes", 
        "⊥-perp", "⋅-sdot", "⌈-lceil", "⌉-rceil", "⌊-lfloor", "⌋-rfloor", "〈-lang", "〉-rang", "◊-loz", "♠-spades", "♣-clubs", "♥-hearts", "♦-diams"
     };
        private static Dictionary<string, char> _lookupTable = GenerateLookupTable();

        // Methods
        private static Dictionary<string, char> GenerateLookupTable()
        {
            Dictionary<string, char> dictionary = new Dictionary<string, char>(StringComparer.Ordinal);
            foreach (string str in _entitiesList)
            {
                dictionary.Add(str.Substring(2), str[0]);
            }
            return dictionary;
        }

        public static char Lookup(string entity)
        {
            char ch;
            _lookupTable.TryGetValue(entity, out ch);
            return ch;
        }
    }
}
查看更多
仙女界的扛把子
3楼-- · 2019-02-04 17:31

Found today from this here little site that HtmlEncode/Decode can be done using System.Net library in C# 4.0 Client Profile:

Uri.EscapeDataString(...)
WebUtility.HtmlEncode(...)

Edit: I re-read that the question applied for the 3.5 Client Framework but maybe this can be useful those who have updated 4.0..

查看更多
Lonely孤独者°
4楼-- · 2019-02-04 17:31

Apparently google couldn't find it either, so they wrote there own api-compatible version:

Here's a workaround:

  1. Compile Google's version as a library

    wget 'http://google-gdata.googlecode.com/svn/trunk/clients/cs/src/core/HttpUtility.cs'
    gmcs -t:library HttpUtility.cs
    
  2. Edit your-project.cs to include that namespace

    using Google.GData.Client; // where HttpUtility lives
    
  3. Recompile using the library

    gmcs your-project.cs -r:System.Web.Services -r:System.Web -r:HttpUtility
    

From glancing at the source code, it appears that this is .NET 2.0-compatible.

I just wrote my first hello world in csharp yesterday and I ran into this problem, so I hope this helps someone else.

查看更多
Explosion°爆炸
5楼-- · 2019-02-04 17:34

I’d strongly not recommend rolling your own encoding. I’d use the Microsoft Anti-Cross Site Scripting Library which is very small (v1.5 is ~30kb) if HttpUtility.HtmlEncode isn’t available.

As for decoding, maybe you could use the decoding routine from Mono?

查看更多
\"骚年 ilove
6楼-- · 2019-02-04 17:34

When addressing Windows Phone and the Desktop (Client profile!) world the fastest way I found is:

        private static string HtmlDecode(string text)
        {
#if WINDOWS_PHONE
            return System.Net.HttpUtility.HtmlDecode(text);
#else
            return System.Net.WebUtility.HtmlDecode(text);
#endif
        }

The odd thing for me was that the namespace is System.Net but the class name differs in the Windows Phone world ... (don't like to use System.Web/full profile when not really needed and the System.Web isn't supported on the Windows Phone platform anyway ...)

查看更多
姐就是有狂的资本
7楼-- · 2019-02-04 17:44

Two main ways :

  1. Deploy using the full .NET Framework
  2. Write your own / 3rd party lib for these functionalities
查看更多
登录 后发表回答