- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
XML Query
Tue, 2009-11-17, 07:59
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
Tue, 2009-11-17, 18:37
#2
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:
--
Daniel C. Sobral
Veni, vidi, veterni.
<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.
Thu, 2009-11-26, 10:37
#3
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
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