8

I'm passing around a Uri.Builder object as a mechanism for subclasses to fill in whatever parameters necessary into a Uri before it is executed in Android.

Problem is, one of the parameters that the base class adds using builder.appendQueryParameter("q",searchPhrase); needs to be replaced in the sub-class, but I can only find appendQueryParameter(), there is no replace or set method. appendQueryParameter() with the same parameter name adds another instance of the parameter, doesn't replace it.

Should I give up and try another way? Or is there a way to replace query parameters that I haven't found yet?

Dhiraj Gupta
  • 9,704
  • 8
  • 49
  • 54

6 Answers6

23

Since there is no in-built method, the best way I have found is to build a new Uri. You iterate over all the query parameters of the old Uri and then replace the desired key with the new value.

private static Uri replaceUriParameter(Uri uri, String key, String newValue) {
    final Set<String> params = uri.getQueryParameterNames();
    final Uri.Builder newUri = uri.buildUpon().clearQuery();
    for (String param : params) {
        newUri.appendQueryParameter(param, 
            param.equals(key) ? newValue : uri.getQueryParameter(param));
    }

    return newUri.build();
}
Nachi
  • 4,218
  • 2
  • 37
  • 58
5

This will add a parameter, or replace an existing parameter's value in Kotlin.

Extension of Uri:

fun Uri.addUriParameter(key: String, newValue: String): Uri {
    val params = queryParameterNames
    val newUri = buildUpon().clearQuery()
    var isSameParamPresent = false
    for (param in params) {
        // if same param is present override it, otherwise add the old param back
        newUri.appendQueryParameter(param,
                if (param == key) newValue else getQueryParameter(param))
        if (param == key) {
            // make sure we do not add new param again if already overridden
            isSameParamPresent = true
        }
    }
    if (!isSameParamPresent) {
        // never overrode same param so add new passed value now
        newUri.appendQueryParameter(key,
                newValue)
    }
    return newUri.build()
}

Implementation:

val appendedURL = originalUri.addUriParameter("UID","123456")
bmjohns
  • 6,344
  • 1
  • 33
  • 39
1

Somewhat more concise way of doing what @bmjohns is suggesting.

fun Uri.addUriParameter(key: String, newValue: String): Uri =
  with(buildUpon()) {
    clearQuery()
    queryParameterNames.forEach {
      if (it != key) appendQueryParameter(it, getQueryParameter(it))
    }
    appendQueryParameter(key, newValue)
    build()
  }
zarsky
  • 680
  • 2
  • 14
  • 24
1
/*
 * Append or replace query parameters
 */
fun Uri.Builder.addQueryParameters(uri: Uri, params: Map<String, String>) = apply {
    if (uri.query == null) {
        appendQueryParameters(params)
    } else {
        clearQuery()
        appendQueryParameters(params)
        val names = params.keys
        uri.queryParameterNames.forEach {
            if (it !in names) appendQueryParameter(it, uri.getQueryParameter(it))
        }
    }
}

fun Uri.Builder.appendQueryParameters(params: Map<String, String>) = apply {
    params.forEach { name, value ->
        appendQueryParameter(name, value)
    }
}
Vitalii Movchan
  • 630
  • 7
  • 4
0

I have same problem ago, and decide to go another way.

Make an wrapper class and store queries (say setQueryParameter(key, value)) in Map (or ArrayMap, something like that).

Then wrapper instance's build() method processes original Builder's appendQueryParameter() and build()

ytRino
  • 1,450
  • 15
  • 28
  • Yeah well, I was already maintaining the parameters in a HashMap, but in my query engine I came upon the need for Array parameters for a particular API and the requirement broke my usage of the HashMap. So I converted everything to use the Uri.Builder directly. And then, this replacement requirement came up. I guess I'll have to do both. :( – Dhiraj Gupta Mar 06 '15 at 11:35
0

With UrlQuerySanitizer -

  val sanitizer = UrlQuerySanitizer(serviceUrl)
  val paramVal = sanitizer.getValue("byCustomValue")
  val replacedUrl = serviceUrl.replace(paramVal, "REPLACE_HERE")

Here is a simple method to encode a specific param -

  fun encodeParameter(url: String, key: String): String {
        val sanitizer = UrlQuerySanitizer(url)
        return if (sanitizer.hasParameter(key)) {
            val paramValue = sanitizer.getValue(key)
            val encodedValue = try {
                URLEncoder.encode(paramValue, "utf-8")
            } catch (e: UnsupportedEncodingException) {
                paramValue
            }
            url.replace(paramValue, encodedValue)
        } else url
    }
Anoop M Maddasseri
  • 10,213
  • 3
  • 52
  • 73
  • What if inside the url there is another char sequence with the same value as the one you are replacing with? – manuel Oct 14 '22 at 06:50