【问题标题】:Decoding URI query string in Java在 Java 中解码 URI 查询字符串
【发布时间】:2011-02-07 14:21:40
【问题描述】:

我需要解码一个包含查询字符串的 URI;预期的输入/输出行为类似于以下内容:

abstract class URIParser
{       
    /** example input: 
      * something?alias=pos&FirstName=Foo+A%26B%3DC&LastName=Bar */
    URIParser(String input) { ... }
    /** should return "something" for the example input */
    public String getPath(); 
    /** should return a map 
      * {alias: "pos", FirstName: "Foo+A&B=C", LastName: "Bar"} */
    public Map<String,String> getQuery();
}

我尝试过使用java.net.URI,但它似乎可以解码查询字符串,所以在上面的示例中,我留下了“alias=pos&FirstName=Foo+A&B=C&LastName=Bar”,因此“是否”存在歧义&" 是查询分隔符或查询组件中的字符。

编辑:我刚刚尝试了URI.getRawQuery(),但它没有进行编码,所以我可以用&amp; 拆分查询字符串,但是我该怎么办? Javascript有decodeURIComponent,Java中好像找不到对应的方法。

有什么建议吗?我不想使用任何新的库。

【问题讨论】:

  • 既然你不想引入新的库,请问你是在什么环境下收到这些URI的?

标签: java decoding query-string


【解决方案1】:

使用

URLDecoder.decode(proxyRequestParam.replace("+", "%2B"), "UTF-8")
          .replace("%2B", "+")

模拟decodeURIComponent。 Java 的URLDecoder 将加号解码为空格,这不是您想要的,因此您需要替换语句。

警告:末尾的.replace("%2B", "+")会损坏您的数据如果原始数据(pre-x-www-form-urlencoded ) 包含该字符串,正如@xehpuk 指出的那样。

【讨论】:

  • 这应该是公认的答案。 URI 按原样处理 + 符号,而空格被编码为 %20。 URLDecoder 与 URI 编码字符串不兼容,因为它会将 + 和 %20 解码为空格。
  • 第二次替换有什么意义?解码后,字符串中将不再有任何“%2B”实例,因为它们都将被替换为“+”,因此替换将没有任何内容。
  • 重点是您不希望在解码字符串中包含编码字符。由于 Java 不会像 JavaScript 那样解码 + 符号,因此我首先对 + 符号进行编码,这样它就不会被 Java 触及,然后将 %2B 解码为 + 符号。简而言之:如果我不这样做,解码后的 URL 将不包含原始的 + 符号(因为 Java 在解码阶段会丢失它们)。
  • @janb - 我认为第二次替换是不必要的,因为decode 方法已经将它找到的任何%2B 转换为+。第一次替换是必要的,以阻止它将+ 转换为空格。
  • @StevePowell 第二次替换不仅没有必要,而且是错误的。
【解决方案2】:

见课程URLDecoder

【讨论】:

  • 应该注意的是,您应该在使用它之前识别查询部分并将参数拆分为键/值对,但它会将百分比编码的值解码为给定的编码(参见 UTF- 8) 根据 HTML application/x-www-form-urlencoded 规范。
  • 总是把答案放在你的答案中。链接会产生额外的工作,并且不能保证链接将始终有效。
【解决方案3】:
var reqParam =  URLDecoder.decode(reqParam, "UTF-8")

【讨论】:

    【解决方案4】:

    关于 + 号的问题:

    我根据@janb 的回答做了一个封装了 URLDecoder 函数的辅助类

    import android.net.Uri;
    import android.support.annotation.Nullable;
    import android.text.TextUtils;
    
    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Locale;
    
    public class DateDecoder {
    
        private static final String KEY_DATE = "datekey";
    
        private static final SimpleDateFormat SIMPLE_DATE_FORMAT =
                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.US);
    
    
        public static void main(String[] args) throws UnsupportedEncodingException {
            try {
                Uri uri = Uri.parse("http://asdf.com?something=12345&" +
                        KEY_DATE +"=2016-12-24T12:00:00+01:00");
    
                System.out.println("parsed date: " + DateDecoder.createDate(uri)); // parsed date: Sat Dec 24 12:00:00 GMT+01:00 2016
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Nullable
        public static Date createDate(@Nullable Uri data) {
            if (data != null) {
                try {
                    String withPlus = decodeButKeepPlus(KEY_DATE, data.getEncodedQuery());
                    if (!TextUtils.isEmpty(withPlus)) {
                        return SIMPLE_DATE_FORMAT.parse(withPlus);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
        /**
         * copied from android.net.Uri.java
         */
        @Nullable
        public static String decodeButKeepPlus(String encodedKey, String completeEncodedQuery)
                throws UnsupportedEncodingException {
    
            final int length = completeEncodedQuery.length();
            int start = 0;
            do {
                int nextAmpersand = completeEncodedQuery.indexOf('&', start);
                int end = nextAmpersand != -1 ? nextAmpersand : length;
    
                int separator = completeEncodedQuery.indexOf('=', start);
                if (separator > end || separator == -1) {
                    separator = end;
                }
    
                if (separator - start == encodedKey.length()
                        && completeEncodedQuery.regionMatches(start, encodedKey, 0, encodedKey.length())) {
                    if (separator == end) {
                        return "";
                    } else {
                        String encodedValue = completeEncodedQuery.substring(separator + 1, end);
                        if (!TextUtils.isEmpty(encodedValue)) {
                            return URLDecoder.decode(encodedValue.replace("+", "%2B"), "UTF-8").replace("%2B", "+");
                        }
                    }
                }
    
                // Move start to end of name.
                if (nextAmpersand != -1) {
                    start = nextAmpersand + 1;
                } else {
                    break;
                }
            } while (true);
            return null;
        }
    
    }
    

    【讨论】:

      【解决方案5】:
      new java.net.URI(proxyRequestParam).getPath()
      

      js encodeURIComponent编码的字符串应该只是一个路径,没有schema等。但是它仍然是 java.net.URI 的有效输入。所以 java.net.URI 会为我们做所有事情,然后它的路径就是我们想要的。

      【讨论】:

      • 虽然此代码可能会解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
      猜你喜欢
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多