6

我有一个用例,我必须解码 URI 的 queryParameter 并执行此操作(超出此问题的范围)。

假设我有一个 URI,我必须对其进行解码。现在我知道目前所有的%20都将转换为空格,并且在创建 URI空间时应该用%20表示,但在某些情况下,我可能会得到%作为空格的 URI 。因此,我想将%转换为空格以保持向后兼容性。最后有一个注释,有助于理解问题。

我试过replaceall() %了,%20但后来又%20变成%2020了,还有很多其他的例外。

这是读取 UPI URI 所必需的,根据 NPCI 的官方文档:

注意:考虑到目前的 PSP 应用程序是为将“%”读取为空格(“”),银行 PSP 应同时支持“%”和“%20”,直到生态系统与修订版保持一致。因此,应确保向后兼容。

编辑 1基于 pshemo 评论 -

我努力了

str.replaceAll("%(?![0-9a-fA-F])","%20")

不满足上述正则表达式的情况是“upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR”

输出是 pn -> Prakash" some othercharacter "mar

4

3 回答 3

1

有趣的问题。%正如您已经看到的那样,您无法可靠地将其替换为空间。您需要有关将通过 uri 传输的内容的更多信息,然后缩小到必须替换的内容和不可以替换的内容,例如

%ZTest -> a space for sure
%Abababtest -> is it a space? probably... but we need to be sure that no strange characters or sequences are allowed
%23th%Affleck%20Street -> space? hex? what is what?

您需要更多信息来可靠地解决该问题,例如:

  1. 哪些是允许的符号?或者哪些是允许解码的十六进制范围?
  2. 哪些查询参数是%作为空格包含的?(所以你可以只改造它们)
  3. 你还需要解码西里尔文、阿拉伯文、汉字吗?
  4. 如果 a%20在 URI 中,我们可以假设 no%将是一个空格吗?或者是否有可能两者都在 URI 中显示为空格?

有了这些附加信息,应该更容易解决问题。

尽管如此,这里有一个解决方案可能会让您朝着正确的方向前进(但请考虑底部的警告!):

Pattern HEX_PATTERN = Pattern.compile("(?i)%([A-F0-9]{2})?");
String CHARSET = "utf-8";
String ENCODED_SPACE = "%20";
String ALLOWED_SYMBOLS = "\\p{L}|\\s|@";

String semiDecode(String uri) throws UnsupportedEncodingException {
    Matcher m = HEX_PATTERN.matcher(uri);
    StringBuffer semiDecoded = new StringBuffer();
    while (m.find()) {
        String match = m.group();
        String hexString = m.group(1);
        String replacementString = match;
        if (hexString == null) {
            replacementString = ENCODED_SPACE;
        } else {
// alternatively to the following just check whether the hex value is in an allowed range... 
// you may want to lookup https://en.wikipedia.org/wiki/List_of_Unicode_characters for this
            String decodedSymbol = URLDecoder.decode(match, CHARSET);
            if (!decodedSymbol.matches(ALLOWED_SYMBOLS)) {
                replacementString = ENCODED_SPACE + hexString;
            }
        }
        m.appendReplacement(semiDecoded, replacementString);
    }
    m.appendTail(semiDecoded);
    return semiDecoded.toString();
}

示例用法:

String uri = "upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR";
String semiDecoded = semiDecode(uri);
System.out.println("Input: " + uri);
System.out.println("Semi-decoded: " + semiDecoded);
System.out.println("Completely decoded query: " + new URI(semiDecoded).getQuery());

这将打印:

Input: upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR
Semi-decoded: upi://pay?pa=praksh%40kmbl&pn=Prakash%20Abmar&cu=INR
Completely decoded query: pa=praksh@kmbl&pn=Prakash Abmar&cu=INR

警告......一些事情要记住:

  • 此特定实现不适用于占用超过 2 个十六进制值的西里尔字母、中文或其他字母(即%##%##%##%##%##单个字符将不再被解码)
  • 您需要根据您的需要调整允许的符号(请参阅ALLOWED_SYMBOLS; 现在它接受任何字母、任何空格和的正则表达式@
  • charset utf-8 被假定为
于 2018-08-31T12:50:07.337 回答
1

可能不是您想要的答案,但这可能会有所帮助:

public class Test {

    public static void main(String... a) {
        try {
            //
            String u = "upi://pay?pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR";
            System.out.println(decode(u));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String decode(String in) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < in.length(); i++) {
            char c = in.charAt(i);
            if (c == '%') {
                int decoded = Integer.parseInt(in.substring(i + 1, i + 3), 16);
                if (decoded >= 32 && decoded <= 126) { //Possible valid char
                    sb.append((char) decoded);
                    i += 2;
                } else { //not a valid char... maybe a space
                    sb.append(" ");
                }
            } else if (c == '+') {
                sb.append(" ");
            } else {
                sb.append(c);
            }
        }

        return sb.toString();
    }
}

有很多可能性,因此您可能需要一个“自定义”解决方案。上面的代码涵盖了一些情况。

于 2018-08-28T11:51:28.067 回答
0

我为此使用的解决方案是不使用 QR 中提供的收款人名称,并使用 vpa 查询 PSP 以获得正确的名称。这样,您还将确保收款人存在。

例如:

  1. 给定 QR 的 URI 为upi://pay?pa=someone@upi&pn=firstname%lastname&cu=INR
  2. 提取 pasomeone@upi并使用它从 PSP 获取用户名
  3. 由于除了名称和注释之外的任何内容都不能包含%或包含%20在其中,只需使用其他答案中提供的任何解决方法或使用更简单的注释解决方案,因为注释通常不太重要。
于 2018-09-06T11:21:28.360 回答