开发者

Filter parameters from query String (using Guava?)

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜