Eine einsatzbereite Lösung für die Dekodierung des URI-Abfrageteils (einschließlich Dekodierung und Multiparameterwerte)
Kommentare
Ich war nicht zufrieden mit dem von @Pr0gr4mm3r bereitgestellten Code in https://stackoverflow.com/a/13592567/1211082 . Die Stream-basierte Lösung führt kein URLDecoding durch, die veränderbare Version ist klumpig.
Daher habe ich eine Lösung erarbeitet, die
- Kann einen URI-Abfrageteil in einen
Map<String, List<Optional<String>>>
- Dose mehrere Werte verarbeiten für denselben Parameternamen
- Dose Parameter ohne Wert richtig darstellen (
Optional.empty()
anstelle von null
)
- Dekodiert Parameternamen y Werte korrekt über
URLdecode
- Basiert auf Java 8 Streams
- Ist direkt verwendbar (siehe Code einschließlich Importe unten)
- Ermöglicht eine ordnungsgemäße Fehlerbehandlung (hier durch Umwandlung einer geprüften Ausnahme
UnsupportedEncodingException
in eine Laufzeitausnahme RuntimeUnsupportedEncodingException
die das Zusammenspiel mit Stream ermöglicht. (Reguläre Funktionen in Funktionen zu verpacken, die geprüfte Ausnahmen auslösen, ist eine Qual. Und Scala Try
ist in der Java-Sprachvoreinstellung nicht verfügbar).
Java-Code
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import static java.util.stream.Collectors.*;
public class URIParameterDecode {
/**
* Decode parameters in query part of a URI into a map from parameter name to its parameter values.
* For parameters that occur multiple times each value is collected.
* Proper decoding of the parameters is performed.
*
* Example
* <pre>a=1&b=2&c=&a=4</pre>
* is converted into
* <pre>{a=[Optional[1], Optional[4]], b=[Optional[2]], c=[Optional.empty]}</pre>
* @param query the query part of an URI
* @return map of parameters names into a list of their values.
*
*/
public static Map<String, List<Optional<String>>> splitQuery(String query) {
if (query == null || query.isEmpty()) {
return Collections.emptyMap();
}
return Arrays.stream(query.split("&"))
.map(p -> splitQueryParameter(p))
.collect(groupingBy(e -> e.get0(), // group by parameter name
mapping(e -> e.get1(), toList())));// keep parameter values and assemble into list
}
public static Pair<String, Optional<String>> splitQueryParameter(String parameter) {
final String enc = "UTF-8";
List<String> keyValue = Arrays.stream(parameter.split("="))
.map(e -> {
try {
return URLDecoder.decode(e, enc);
} catch (UnsupportedEncodingException ex) {
throw new RuntimeUnsupportedEncodingException(ex);
}
}).collect(toList());
if (keyValue.size() == 2) {
return new Pair(keyValue.get(0), Optional.of(keyValue.get(1)));
} else {
return new Pair(keyValue.get(0), Optional.empty());
}
}
/** Runtime exception (instead of checked exception) to denote unsupported enconding */
public static class RuntimeUnsupportedEncodingException extends RuntimeException {
public RuntimeUnsupportedEncodingException(Throwable cause) {
super(cause);
}
}
/**
* A simple pair of two elements
* @param <U> first element
* @param <V> second element
*/
public static class Pair<U, V> {
U a;
V b;
public Pair(U u, V v) {
this.a = u;
this.b = v;
}
public U get0() {
return a;
}
public V get1() {
return b;
}
}
}
Scala-Code
... und der Vollständigkeit halber kann ich nicht widerstehen, die Lösung in Scala anzubieten, die durch Kürze und Schönheit besticht
import java.net.URLDecoder
object Decode {
def main(args: Array[String]): Unit = {
val input = "a=1&b=2&c=&a=4";
println(separate(input))
}
def separate(input: String) : Map[String, List[Option[String]]] = {
case class Parameter(key: String, value: Option[String])
def separateParameter(parameter: String) : Parameter =
parameter.split("=")
.map(e => URLDecoder.decode(e, "UTF-8")) match {
case Array(key, value) => Parameter(key, Some(value))
case Array(key) => Parameter(key, None)
}
input.split("&").toList
.map(p => separateParameter(p))
.groupBy(p => p.key)
.mapValues(vs => vs.map(p => p.value))
}
}