XML/Input: Difference between revisions

From Rosetta Code
< XML
Content added Content deleted
(→‎{{header|AWK}}: add solution using getXML.awk)
(→‎{{header|AWK}}: add solution using xmlparser.awk)
Line 110: Line 110:
{{works with|gawk}} or {{works with|nawk}}
{{works with|gawk}} or {{works with|nawk}}
<lang sh>awk -f getXML.awk sample.xml | awk '
<lang sh>awk -f getXML.awk sample.xml | awk '
$1 == "TAG" {
$1 == "TAG" {tag = $2}
tag == "Student" && /Name=/ {print substr($0, index($0, "=") + 1)}
tag = $2
}
tag == "Student" && /Name=/ {
print substr($0, index($0, "=") + 1)
}
'</lang>
'</lang>
Using [http://home.vrweb.de/~juergen.kahrs/gawk/XML/xmlgawk.html#Steve-Coile_0027s-xmlparse_002eawk-script xmlparser.awk] by Steve Coile, one can do this:
producing this output

{{works with|gawk}}
<lang sh>gawk -f xmlparser.awk sample.xml | awk '
$1 == "begin" {tag = $2}
$1 == "attrib" {attrib = $2}
$1 == "value" && tag == "STUDENT" && attrib == "name" {print $2}
'</lang>

Both of these produce this output
<pre>April
<pre>April
Bob
Bob

Revision as of 15:18, 3 June 2009

Task
XML/Input
You are encouraged to solve this task according to the task description, using any language you may know.

Given the below XML fragment, extract the list of student names using whatever means desired. If the only viable method is to use XPath, refer the reader to the task XML and XPath.

<lang xml><Students>

 <Student Name="April" Gender="F" DateOfBirth="1989-01-02" />
 <Student Name="Bob" Gender="M"  DateOfBirth="1990-03-04" />
 <Student Name="Chad" Gender="M"  DateOfBirth="1991-05-06" />
 <Student Name="Dave" Gender="M"  DateOfBirth="1992-07-08">
   <Pet Type="dog" Name="Rover" />
 </Student>
 <Student DateOfBirth="1993-09-10" Gender="F" Name="Émily" />

</Students></lang>

Expected Output

April
Bob
Chad
Dave
Émily

ActionScript

<lang actionscript> package {

   import flash.display.Sprite;
   public class XMLReading extends Sprite
   {
       public function XMLReading()
       {
           var xml:XML = <Students>
                           <Student Name="April" />
                           <Student Name="Bob" />
                           <Student Name="Chad" />
                           <Student Name="Dave" />
                           <Student Name="Emily" />
                         </Students>;
           for each(var node:XML in xml..Student)
           {
               trace(node.@Name);
           }
       }
   }

} </lang>

AWK

The following code extracts the value of the property "Name" from every Student tag. It does not handle the &#CODE;; this can be left to others: a way to cope with it fastly, is to output a very simple HTML structure, so that the interpretation is left to an HTML reader/browser.

<lang awk>function parse_buf() {

   if ( match(buffer, /<Student[ \t]+[^>]*Name[ \t]*=[ \t]*"([^"]*)"/, mt) != 0 ) {
     students[mt[1]] = 1
   }
   buffer = ""

}

BEGIN {

 FS=""
 mode = 0
 buffer = ""
 li = 1

}

mode==1 {

 for(i=1; i <= NF; i++) {
   buffer = buffer $i
   if ( $i == ">" ) {
     mode = 0;
     break;
   }
 }
 if ( mode == 0 ) {
   li = i
 } else {
   li = 1
 }
 # let us process the buffer if "complete"
 if ( mode == 0 ) {
   parse_buf()
 }

}

mode==0 {

 for(i=li; i <= NF; i++) {
   if ( $i == "<" ) {
     mode = 1
     break;
   }
 }
 for(j=i; i <= NF; i++) {
   buffer = buffer $i
   if ( $i == ">" ) {
     mode = 0
     parse_buf()
   }
 }
 li = 1

}

END {

 for(k in students) {
   print k
 }

}</lang> Using getXML.awk written by Jan Weber, one could do this:

Works with: gawk

or

Works with: nawk

<lang sh>awk -f getXML.awk sample.xml | awk '

   $1 == "TAG"                 {tag = $2}
   tag == "Student" && /Name=/ {print substr($0, index($0, "=") + 1)}

'</lang> Using xmlparser.awk by Steve Coile, one can do this:

Works with: gawk

<lang sh>gawk -f xmlparser.awk sample.xml | awk '

   $1 == "begin"                                         {tag = $2}
   $1 == "attrib"                                        {attrib = $2}
   $1 == "value" && tag == "STUDENT" && attrib == "name" {print $2}

'</lang>

Both of these produce this output

April
Bob
Chad
Dave
&#x00C9;mily

C

Library: LibXML

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>
  3. include <libxml/parser.h>
  4. include <libxml/tree.h>

static void print_names(xmlNode *node) {

 xmlNode *cur_node = NULL;
 for (cur_node = node; cur_node; cur_node = cur_node->next) {
   if (cur_node->type == XML_ELEMENT_NODE) {
     if ( strcmp(cur_node->name, "Student") == 0 ) {

xmlAttr *prop = NULL; if ( (prop = xmlHasProp(cur_node, "Name")) != NULL ) { printf("%s\n", prop->children->content);

}

     }
   }
   print_names(cur_node->children);
 }

}

const char *buffer =

 "<Students>\n"
 "<Student Name=\"April\" />\n"
 "<Student Name=\"Bob\" />\n"
 "<Student Name=\"Chad\" />\n"
 "<Student Name=\"Dave\" />\n"
 "<Student Name=\"Emily\" />\n"
 "</Students>\n";

int main() {

 xmlDoc *doc = NULL;
 xmlNode *root = NULL;
 doc = xmlReadMemory(buffer, strlen(buffer), NULL, NULL, 0);
 if ( doc != NULL ) {
   root = xmlDocGetRootElement(doc);
   print_names(root);
   xmlFreeDoc(doc);
 }
 xmlCleanupParser();
 return 0;

}</lang>

J

J's system includes several XML processing libraries. This task is probably best addressed using XPath (this is the type of problem XPath was designed to solve), but the task description implicitly discourages that method. So we can use the SAX library instead:

<lang j> load'xml/sax'

   saxclass 'Students'
   startElement =: ([: smoutput 'Name' getAttribute~ [)^:('Student'-:])
   cocurrent'base'

   process_Students_ XML</lang>
April
Bob
Chad
Dave
Emily

and the definition of XML: <lang j> XML =: noun define

<Students>
  <Student Name="April" />
  <Student Name="Bob" />
  <Student Name="Chad" />
  <Student Name="Dave" />
  <Student Name="Emily" />
</Students>
)</lang>

OCaml

<lang ocaml>

  1. #directory "+site-lib/xml-light" (* or maybe just "+xml-light" *) ;;
  2. #load "xml-light.cma" ;;
  1. let x = Xml.parse_string "
 <Students>
   <Student Name=\"April\" />
   <Student Name=\"Bob\" />
   <Student Name=\"Chad\" />
   <Student Name=\"Dave\" />
   <Student Name=\"Emily\" />
 </Students>"
 in
 Xml.iter (function
   (Xml.Element ("Student", [("Name", name)], [])) -> print_endline name
 |  _ -> ()) x
 ;;

April Bob Chad Dave Emily - : unit = () </lang>

Python

<lang python>import xml.dom.minidom

doc = """<Students>

 <Student Name="April" />
 <Student Name="Bob" />
 <Student Name="Chad" />
 <Student Name="Dave" />
 <Student Name="Emily" />

</Students>"""

doc = xml.dom.minidom.parseString(doc)

for i in doc.getElementsByTagName("Student"):

   print i.getAttribute("Name")</lang>

Tcl

Using

Library: tDOM

<lang tcl>package require tdom set tree [dom parse $xml] set studentNodes [$tree getElementsByTagName Student] ;# or: set studentNodes [[$tree documentElement] childNodes]

foreach node $studentNodes {

   puts [$node getAttribute Name]

} </lang>

Using

Library: TclXML

<lang tcl>package require xml set parser [xml::parser -elementstartcommand elem] proc elem {name attlist args} {

   if {$name eq "Student"} {
       puts [dict get $attlist Name]
   }

} $parser parse $xml</lang>

Using just pure-Tcl (originally on http://wiki.tcl.tk/3919): <lang Tcl>proc xml2list xml {

   regsub -all {>\s*<} [string trim $xml " \n\t<>"] "\} \{" xml
   set xml [string map {> "\} \{#text \{" < "\}\} \{"}  $xml]
   set res ""   ;# string to collect the result
   set stack {} ;# track open tags
   set rest {}
   foreach item "{$xml}" {
       switch -regexp -- $item {

^# {append res "{[lrange $item 0 end]} " ; #text item} ^/ { regexp {/(.+)} $item -> tagname ;# end tag set expected [lindex $stack end] set stack [lrange $stack 0 end-1] append res "\}\} "

           }

/$ { # singleton - start and end in one <> group

               regexp {([^ ]+)( (.+))?/$} $item -> tagname - rest
               set rest [lrange [string map {= " "} $rest] 0 end]
               append res "{$tagname [list $rest] {}} "

} default {

               set tagname [lindex $item 0] ;# start tag
               set rest [lrange [string map {= " "} $item] 1 end]
               lappend stack $tagname
               append res "\{$tagname [list $rest] \{"

}

       }
   }
   string map {"\} \}" "\}\}"} [lindex $res 0]   ;#"

} proc deent str {

   regsub -all {&\#x(.+?);} $str {\\u\1} str
   subst -nocommands -novar $str

}

  1. ----------------------- Testing the whole thing:

set xml {<Students>

 <Student Name="April" Gender="F" DateOfBirth="1989-01-02" />
 <Student Name="Bob" Gender="M"  DateOfBirth="1990-03-04" />
 <Student Name="Chad" Gender="M"  DateOfBirth="1991-05-06" />
 <Student Name="Dave" Gender="M"  DateOfBirth="1992-07-08">
   <Pet Type="dog" Name="Rover" />
 </Student>
 <Student DateOfBirth="1993-09-10" Gender="F" Name="Émily" /></Students>

} foreach i [lindex [xml2list $xml] 2] {

   if {[lindex $i 0] eq "Student"} {
       foreach {att val} [lindex $i 1] {
           if {$att eq "Name"} {puts [deent $val]}
       }
   }

}

</lang>

Vedit macro language

This implementation finds all Student tags and then displays the contents of their Name parameter. <lang vedit> Repeat(ALL) {

   Search("<Student ", ERRBREAK)
   #1 = Cur_Pos
   Match_Paren()
   if (Search_Block('Name="', #1, Cur_Pos, BEGIN+ADVANCE+NOERR+NORESTORE)==0) { Continue }
   #2 = Cur_Pos
   Search('"')
   Type_Block(#2, Cur_Pos)
   Type_Newline

} </lang>

Output:

April
Bob
Chad
Dave
Émily

Visual Basic .NET

<lang vbnet>Dim xml = <Students>

             <Student Name="April"/>
             <Student Name="Bob"/>
             <Student Name="Chad"/>
             <Student Name="Dave"/>
             <Student Name="Emily"/>
          </Students>

Dim names = (From node In xml...<Student> Select node.@Name).ToArray

For Each name In names

    Console.WriteLine(name)

Next </lang>