Take this开发者_开发技巧 HTTP query String:
foo=fooValue&bar=barValue&phleem=phleemValue
Or this one:
bar=barValue&phleem=phleemValue&foo=someOtherFoo
What would be the best way to remove the foo
parameter?
All Java solutions welcome, but Guava preferred.
(There is no ServletContext available, so Servlet methods won't help)
Update: The method should handle multiple parameters to remove.
This is not the most elegant solution, but works as you expect:
private String removeParameter(String string, final String parameterName) {
List<String> list = newArrayList(string.split("&"));
Collection<String> filtered = Collections2.filter(list, new Predicate<String>() {
public boolean apply(String s) {
return !s.startsWith(parameterName + "=");
}
});
return Joiner.on("&").join(filtered);
}
UPDATE
To handle multiple parameters:
@Test
public void removesMultipleParametersFromQuery() throws Exception {
String result = removeParameters("foo=fooValue&zee=lalal&bar=barValue&phleem=phleemValue", "foo", "bar");
assertThat(result, is("zee=lalal&phleem=phleemValue"));
}
private String removeParameters(String query, final String...parameterNames) {
List<String> list = newArrayList(query.split("&"));
return Joiner.on("&").join(filter(list, startsWithAnyOf(parameterNames)));
}
private Predicate<String> startsWithAnyOf(final String[] parameterNames) {
return new Predicate<String>() {
public boolean apply(String s) {
return !Iterables.any(newArrayList(parameterNames), isPrefixOf(s));
}
};
}
private Predicate<String> isPrefixOf(final String string){
return new Predicate<String>() {
public boolean apply(String candidate) {
return string.startsWith(candidate);
}
};
}
Here is my own solution (but it's ugly as hell):
public static String removeParameters(final String queryString,
final String... paramNames){
Iterable<Predicate<CharSequence>> innerPredicates;
if(paramNames.length == 0){
innerPredicates = Collections.emptySet();
} else{
innerPredicates =
Iterables.transform(
Arrays.asList(paramNames),
new Function<String, Predicate<CharSequence>>(){
@Override
public Predicate<CharSequence> apply(
final String input){
return Predicates.contains(
Pattern.compile("^"
+ Pattern.quote(input) + "=")
);
}
});
}
final Predicate<CharSequence> predicate =
Predicates.not(Predicates.or(innerPredicates));
return Joiner.on("&").join(
Iterables.filter(Splitter.on('&').split(queryString), predicate));
}
I would have something to read the query string in as a Multimap<String, String>
on one end and something to write a Multimap<String, String>
in query string format on the other. Then doing this is as simple as calling removeAll(parameter)
for each parameter you want to remove from the query string. Don't deal with something like that as a raw String
except at the points where you absolutely have to.
Here's some code for that:
private static final Splitter QUERY_SPLITTER = Splitter.on(CharMatcher.anyOf("&;"));
private static final Joiner QUERY_JOINER = Joiner.on('&');
private static final EntrySplitFunction ENTRY_SPLITTER =
new EntrySplitFunction(Splitter.on('='));
private static final EntryJoinFunction ENTRY_JOINER =
new EntryJoinFunction(Joiner.on('=').useForNull(""));
public static Multimap<String, String> parseQueryString(String queryString) {
Multimap<String, String> result = HashMultimap.create();
Iterable<String> entryStrings = QUERY_SPLITTER.split(queryString);
for (Map.Entry<String, String> entry : transform(entryStrings, ENTRY_SPLITTER)) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
public static String toQueryString(Multimap<String, String> multimap) {
return QUERY_JOINER.join(transform(multimap.entries(), ENTRY_JOINER));
}
private static class EntrySplitFunction
implements Function<String, Map.Entry<String, String>> {
private final Splitter keyValueSplitter;
private EntrySplitFunction(Splitter keyValueSplitter) {
this.keyValueSplitter = keyValueSplitter;
}
@Override public Map.Entry<String, String> apply(String input) {
Iterator<String> keyAndValue = keyValueSplitter.split(input).iterator();
return Maps.immutableEntry(keyAndValue.next(), keyAndValue.next());
}
}
private static class EntryJoinFunction
implements Function<Map.Entry<String, String>, String> {
private final Joiner keyValueJoiner;
private EntryJoinFunction(Joiner keyValueJoiner) {
this.keyValueJoiner = keyValueJoiner;
}
@Override public String apply(Map.Entry<String, String> input) {
return keyValueJoiner.join(input.getKey(), input.getValue());
}
}
With this, all you'd have to do to implement your method is:
public static String removeParameters(String queryString, String... parameters) {
Multimap<String, String> query = parseQueryString(queryString);
for (String parameter : parameters) {
query.removeAll(parameter);
}
return toQueryString(query);
}
Better yet, assuming there's anything else you do with the query, you'd just have the Multimap
already when you reach the point that you need to do this and you wouldn't even bother writing a special method for it.
You can use Guava
public static String toQueryString(final Map<String, Object> queryParameters) {
return "?" + Joiner.on('&').withKeyValueSeparator("=").join(queryParameters);
}
精彩评论