This page is no longer maintained — Please continue to the home page at www.scala-lang.org

XML Query

3 replies
Christopher Schmidt
Joined: 2009-11-09,
User offline. Last seen 42 years 45 weeks ago.

Hello, I have f.e. this XML file:

1.234

5.678

1.234

5.678

In Java I used this xPath query...

"//daotype/dao[@id='" + daoId + "']/property"

...to get all the "property"-nodes and put them into a HashMap with "daoId"
as key.

Are there any examples how to do this in Scala?

Regards CS

Florian Hars 2
Joined: 2009-11-01,
User offline. Last seen 42 years 45 weeks ago.
Re: XML Query

Christopher Schmidt schrieb:
> Are there any examples how to do this in Scala?

scala> val daos = [... your data here ...]
daos: scala.xml.Elem =
[... your data here ...

scala> Map() ++ (for (car <- daos \\ "dao") yield (car \ "@id" text, Map() ++ (for(prop <- car \ "property") yield (prop \ "@name" text, prop \ "value" text))))
res18: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,String]] = Map(Car -> Map(longitude -> 1.234, latitude -> 5.678), Car2 -> Map(longitude2 -> 1.234, latitude2 -> 5.678))

- Florian

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: XML Query
You haven't said if "id" always exist or not. Let's start with the very basic:   res0 \\ "daotype" \ "dao" map (dao => (dao attribute "id") -> (dao \ "property"))   I get all "dao", and map them into the attribute "id" and the "property". See the result below, and pay attention to the type;   List((Some(Car),<property name="longitude">
                      <value>1.234</value>
                  </property><property name="latitude">
                      <value>5.678</value>
                  </property>), (Some(Car2),<property name="longitude2">
                      <value>1.234</value>
                  </property><property name="latitude2">
                      <value>5.678</value>
                  </property>))   Note that when I get the attribute, not only I receive an "Option" back, but an Option of a Seq[Node]. You can call "text" on it to get the text back, though, without worrying about the Seq. At any rate, let's assume you do need to filter. You can do that with filter or, in Scala 2.8, a filterMap which, at the same time, get things out of the Option. But let's see an alternative way of doing that:   for ((Some(key), value) <- res0 \\ "daotype" \ "dao" map (dao => (dao attribute "id") -> (dao \ "property")))
yield key.text -> value   That goes most of the way. However, it mixes a "map" with a for comprehension. Since for comprehensions are just syntactic sugars for "map", "flatMap", "filter" and "foreach", it stands to reason that we can remove that map and improve the the overall expression:   for {
  dao <-  res0 \\ "daotype" \ "dao"
  key <- dao attribute "id"
  value = dao \ "property"
} yield key.text -> value

You might wonder where did the Option go. Well, an Option can be treated as a collection which has either zero or one elements. In other words, "key <- Some(stuff)" is pretty similar to "key <- List(stuff)". That expression above returns scala.collection.immutable.Seq[(String, scala.xml.NodeSeq)]. So, how does one add it to a Map? Well, it depends on whether your Map is mutable or immutable. For instance, with a mutable Map you can do the following:   val map = scala.collection.mutable.Map.empty[String,scala.xml.NodeSeq] for {
  dao <-  res0 \\ "daotype" \ "dao"
  key <- dao attribute "id"
  value = dao \ "property"
} map += key.text -> value   With an immutable Map, the usual way to do it is:   val map = Map.empty[String,scala.xml.NodeSeq] ++ (for {
  dao <-  res0 \\ "daotype" \ "dao"
  key <- dao attribute "id"
  value = dao \ "property"
} yield key.text -> value)   Which will result in:   map: scala.collection.immutable.Map[String,scala.xml.NodeSeq] =
Map(Car -> <property name="longitude">
                      <value>1.234</value>
                  </property><property name="latitude">
                      <value>5.678</value>
                  </property>, Car2 -> <property name="longitude2">
                      <value>1.234</value>
                  </property><property name="latitude2">
                      <value>5.678</value>
                  </property>)   Finally, do you notice all those spaces? You may want to get rid of them before storing the XML. You can do it like this:   val map = Map.empty[String,scala.xml.NodeSeq] ++ (for {
  dao <-  res0 \\ "daotype" \ "dao"
  key <- dao attribute "id"
  value = dao \ "property"
} yield key.text -> value.map(scala.xml.Utility.trim))
HTH.   On Tue, Nov 17, 2009 at 5:00 AM, Christopher Schmidt <Christopher.Schmidt@eads.com> wrote:
Hello, I have f.e. this XML file:

<daotype>
       <dao id="Car" class="dao.Car">
           <property name="longitude">
               <value>1.234</value>
           </property>
           <property name="latitude">
               <value>5.678</value>
           </property>
       </dao>
   <dao id="Car2" class="dao.Car2">
           <property name="longitude2">
               <value>1.234</value>
           </property>
           <property name="latitude2">
               <value>5.678</value>
           </property>
       </dao>
   </daotype>

In Java I used this xPath query...

"//daotype/dao[@id='" + daoId + "']/property"

...to get all the "property"-nodes and put them into a HashMap with "daoId"
as key.

Are there any examples how to do this in Scala?

Regards CS




--
Daniel C. Sobral

Veni, vidi, veterni.
ray.mcdermott
Joined: 2009-09-16,
User offline. Last seen 42 years 45 weeks ago.
xpath (again)

Guys,

Following the previous post, I would also like to use the xpath.

In this case I prefer to leverage support from Java rather than the Scala options presented earlier.  My main motivation is that I can reduce the amount of boiler plate code needed to evaluate expressions (which is one of the goals of Scala right?)

Thus I would like:

def findNodeFromXpathExpression(expression: String, seq:  NodeSeq) : Node = {
    xpath.evaluate(expression, seq, XPathConstants.NODE) match {
      case node: Node => return node
      case _ => throw new Exception("xpath cannot find node from expression " + expression)
    }
}


call using:
    findNodeFromXpathExpression("//*[@id='foo']/bar", seq)

So far so nice, but I get a problem ... since the seq is not XML Document :-/

Caused by: java.lang.NullPointerException
                at org.apache.xpath.axes.AxesWalker.setRoot(AxesWalker.java:221)
                at org.apache.xpath.axes.WalkingIterator.setRoot(WalkingIterator.java:
157)
                at org.apache.xpath.axes.NodeSequence.setRoot(NodeSequence.java:265)
                at org.apache.xpath.axes.LocPathIterator.execute(LocPathIterator.java:
212)
                at org.apache.xpath.XPath.execute(XPath.java:337)
                ... 19 more

What is the simplest way to form the NodeSeq as an XML doc?

Thanks in advance

Ray













Florian Hars <fh+scala@hars.de>
17/11/2009 17:18 To Christopher Schmidt <Christopher.Schmidt@eads.com> cc "scala-user@listes.epfl.ch" <scala-user@listes.epfl.ch> Subject Re: [scala-user] XML Query




Christopher Schmidt schrieb:
> Are there any examples how to do this in Scala?

scala> val daos = <daotype>  [... your data here ...] </daotype>
daos: scala.xml.Elem =
<daotype> [... your data here ...

scala> Map() ++ (for (car <- daos \\ "dao") yield (car \ "@id" text, Map() ++ (for(prop <- car \ "property") yield (prop \ "@name" text, prop \ "value" text))))
res18: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,String]] = Map(Car -> Map(longitude -> 1.234, latitude -> 5.678), Car2 -> Map(longitude2 -> 1.234, latitude2 -> 5.678))


- Florian


Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland