Декодирование строки запроса URI в Java

Мне нужно декодировать 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(), и он не выполняет кодировку, поэтому я могу разделить строку запроса с помощью &, но что мне тогда делать? Javascript имеет decodeURIComponent, я не могу найти соответствующий метод в Джава.

Какие-либо предложения? Я бы предпочел не использовать какие-либо новые библиотеки.


person Jason S    schedule 13.04.2010    source источник
comment
Поскольку вы не хотите вводить новые библиотеки, могу я спросить, в какой среде вы получаете эти URI?   -  person stacker    schedule 13.04.2010


Ответы (4)


См. класс URLDecoder.

person Maurice Perry    schedule 13.04.2010
comment
Следует отметить, что вы должны определить часть запроса и разделить параметры на пары ключ/значение перед использованием этого, но он будет декодировать значения с процентным кодированием в заданную кодировку (см. UTF-8) в соответствии со спецификацией HTML application/x-www-form-urlencoded. . - person McDowell; 14.04.2010
comment
Всегда помещайте ответ в свой ответ. Ссылка создает дополнительную работу, и нет гарантии, что ссылка всегда будет работать. - person fivedogit; 14.09.2019

Использовать

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

для имитации decodeURIComponent. Java URLDecoder декодирует знак плюса в пробел, а это не то, что вам нужно, поэтому вам нужны операторы замены.

Внимание! .replace("%2B", "+") в конце испортит ваши данные, если оригинал (pre-x-www-form-urlencoded) содержал эту строку, как указал @xehpuk.

person janb    schedule 03.08.2011
comment
Это должен быть принятый ответ. URI обрабатывают символ + как есть, тогда как пробелы кодируются в %20. URLDecoder не совместим со строками в кодировке URI, поскольку он будет декодировать как +, так и %20 в пробел. - person Kosta; 17.04.2012
comment
В чем смысл второй замены? После декодирования в строке больше не будет экземпляров %2B, поскольку все они будут заменены на +, поэтому замена не будет соответствовать чему-либо. - person David Conrad; 16.08.2012
comment
Дело в том, что вам не нужны закодированные символы в декодированной строке. Поскольку Java не декодирует знак +, как JavaScript, я сначала кодирую знак +, чтобы он не был затронут Java, а затем декодирую %2B в знак +. Короче говоря: если бы я этого не сделал, декодированный URL-адрес не содержал бы исходных знаков + (поскольку Java потеряла бы их на этапе декодирования). - person janb; 21.08.2012
comment
@janb - я думаю, что вторая замена не нужна, потому что метод decode уже преобразует любые найденные %2B в +. Первая замена необходима, чтобы остановить преобразование + в пробелы. - person Steve Powell; 11.09.2013
comment
@StevePowell Вторая замена не только не нужна, но и неверна. - person xehpuk; 18.02.2015
comment
@xehpuk, @StevePowell: в некоторых ситуациях (например, в той, которую я описал выше) это необходимо, потому что вы не хотите терять какой-либо символ «+», если он намеренно находится во входящем параметре. Используя вторую замену, вы имитируете поведение JavaScript decodeURIComponent. - person janb; 10.06.2016
comment
Например, строка "%252B" будет неправильно декодирована вашим решением как "+", а decodeURIComponent("%252B") === "%@B". Есть ли пример, демонстрирующий необходимость последней замены? - person Franklin Yu; 23.05.2018

По поводу проблемы со знаком +:

Я создал вспомогательный класс, который обертывает функцию URLDecoder на основе ответа @janb.

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

}
person JoachimR    schedule 01.04.2016

new java.net.URI(proxyRequestParam).getPath()

Строка, закодированная js encodeURIComponent, должна быть просто путем, без схемы и прочего. Однако это все еще допустимый ввод для java.net.URI. Таким образом, java.net.URI сделает все за нас, и тогда путь к нему будет тем, что нам нужно.

person vipcxj    schedule 11.05.2020
comment
Хотя этот код может решить проблему, включая объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщение и, вероятно, приведет к большему количеству голосов. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для того, кто задает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения и указать, какие ограничения и предположения применяются. - person double-beep; 14.05.2020