Декодировать% в пространство, используя URLDecoder в Java?

У меня есть сценарий использования, в котором я должен декодировать queryParameter URI и делать что-то (вне контекста этого вопроса).

Предположим, у меня есть URI, и я должен его декодировать. Теперь я знаю, что в настоящее время все %20 будут преобразованы в пространство, и при создании пространства URI должно быть представлено %20, но может быть случай, когда я могу получить URI с % в качестве пространства. Поэтому я хочу преобразовать % в пространство, чтобы обеспечить обратную совместимость. В конце есть примечание, которое поможет понять вопрос.

Я старался replaceall() % с %20 но опять же %20 станет %2020 и много других исключений.

Это необходимо для чтения URI UPI. Согласно официальным документам NPCI:

Примечание. Учитывая, что текущие приложения PSP разработаны для чтения "%" в качестве пробела (""), PSP Банка должен поддерживать как "%", так и "%20", пока экосистема не будет приведена в соответствие с пересмотром. Следовательно, обратная совместимость должна быть обеспечена.

РЕДАКТИРОВАТЬ 1 На основе пшемо комментария -

я пытался

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

Случай, который не удовлетворяет приведенному выше регулярному выражению: "upi://pay? Pa=praksh%40kmbl&pn=Prakash%Abmar&cu=INR"

вывод pn -> Prakash"какой-то другой символ" мар

3 ответа

Возможно, это не тот ответ, который вам нужен, но это может помочь:

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

Возможностей много, поэтому, вероятно, вам понадобится "нестандартное" решение. Приведенный выше код охватывает некоторые случаи.

Интересная проблема. Вы не можете заменить % в космос надежно, как вы уже видели себя. Вам нужна дополнительная информация о том, что будет транспортироваться через 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. если %20 находится в URI, можем ли мы предположить, что нет % будет пространство тогда? или возможно, что оба появятся как пространство в 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 предполагалось

Решение, которое я использовал для этого, состоит в том, чтобы не использовать имя получателя, указанное в QR, и запросить PSP с помощью vpa, чтобы получить правильное имя. Таким образом, вы также убедитесь, что получатель платежа существует.

например:

  1. данный QR имеет URI как upi://pay?pa=someone@upi&pn=firstname%lastname&cu=INR
  2. извлечь па, который является someone@upi и использовать его, чтобы получить имя пользователя из PSP
  3. как что-либо кроме имени и примечания не может иметь % или же %20 в нем просто используйте любой из обходных путей, представленных в других ответах, или используйте более простые решения для заметок, поскольку заметки обычно менее важны.
Другие вопросы по тегам