play-ws icon indicating copy to clipboard operation
play-ws copied to clipboard

Order of Form Parameters is not preserved (application/x-www-form-urlencoded)

Open Marc2017 opened this issue 5 years ago • 5 comments

Play WS Version (2.7.2 / etc) JAVA

In StandaloneAhcWSRequest the order of Form-Parameters is not preserved:


                    // XXX shouldn't the encoding be same as charset?
                    Map<String, List<String>> stringListMap = FormUrlEncodedParser.parseAsJava(stringBody, "utf-8");
                    stringListMap.forEach((key, values) -> values.forEach(value -> builder.addFormParam(key, value)));

"FormUrlEncodedParser.parseAsJava" does not preserver the order, and "stringListMap" is unordered.

Marc2017 avatar Jan 20 '20 17:01 Marc2017

So the call is from https://github.com/playframework/play-ws/blob/master/play-ahc-ws-standalone/src/main/java/play/libs/ws/ahc/StandaloneAhcWSRequest.java#L450

Looking at https://github.com/playframework/play-ws/blob/master/play-ahc-ws-standalone/src/main/scala/play/api/libs/ws/ahc/FormUrlEncodedParser.scala#L47 it certainly seems like parseAsJava is ordered.

Can you put together a test case?

wsargent avatar Jan 20 '20 20:01 wsargent

Or is it that the asJava method doesn't retain sorting order?

If you call

  def parseAsJava(data: String, encoding: String): java.util.Map[String, java.util.List[String]] = {
    import scala.collection.JavaConverters._
    val insertHashMap = new util.LinkedHashMap[String, java.util.List[String]]()
    parse(data, encoding).foreach {
      case (key, values) =>
        insertHashMap.put(key, values.asJava)
    }
    insertHashMap
  }

  def parseAsJavaArrayValues(data: String, encoding: String): java.util.Map[String, Array[String]] = {
    val insertHashMap = new util.LinkedHashMap[String, Array[String]]()
    parse(data, encoding).foreach {
      case (key, values) =>
        insertHashMap.put(key, values.toArray)
    }
    insertHashMap
  }

does that change things for you?

wsargent avatar Jan 20 '20 20:01 wsargent

Hi,

seems I have a different implementation:

def parseAsJava(data: String, encoding: String): java.util.Map[String, java.util.List[String]] = { import scala.collection.JavaConverters._ parse(data, encoding).map { case (key, values) => key -> values.asJava }.asJava }

there is no "foreach", there is a "map" - which will change the order.

I´ll check my revision.

Thanks! Marc

Marc2017 avatar Jan 21 '20 07:01 Marc2017

Did you change it to "foreach" in your example? In the current revision there is also a "map".

Marc2017 avatar Jan 21 '20 08:01 Marc2017

@Marc2017 yeah, the example above is using a foreach because it uses an insertion order linked hash map. It's not what's in the master branch.

wsargent avatar Jan 21 '20 17:01 wsargent