homeUnix & Internet Unix & Shell-Programmierung: Ein Scanner für XML Dokumente Prof. Dr. Uwe Schmidt FH Wedel

Ein Scanner für XML Dokumente

weiter

weiter

XML Parser

Scanner
für XML mit Hilfe eines regulären Ausdrucks für die Beschreibung der einfachen XML Sprachbestandteile und die Zerlegung eines Texts mit der scan Methode
Bestandteile
eines XML Dokuments
Beispiel
Eine einfache XHTML-Seite
 
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>11. M&auml;rz 2007: Geh'n wir mal zu Hagenbeck</title>
<link rel="stylesheet" type="text/css" href="../config/html-1400x1050.css"/>
<script type="text/javascript">
<!--
var duration = 7000 * 1.0;
...
-->
</script>
<script
type="text/javascript"
src="../config/html-album.js"
charset="ISO-8859-1"><!-- firefox hack --></script>
</head>
<body onload="initAlbum();" class="album" id="theAlbumBody">
<table class="head">
<tr>
<td class="head1">
<img src="../160x120/Hagenbeck.jpg"
class="icon"
title="11. M&auml;rz 2007: Geh&apos;n wir mal zu Hagenbeck"/>
</td>
<td class="head2">
<div class="title">
11. M&auml;rz 2007: Geh'n wir mal zu Hagenbeck
</div>
<div class="subtitle">
bei herrlichem Fr&uuml;hlingswetter einmal durch den
Tierpark
</div>
</td>
</tr>
</table>
<div class="ruler"/>
<div class="album-content">
...
</div>
<div class="ruler"><!-- firefox hack --></div>
</body>
</html>
1. Schritt
Lexikaische Elemente von XML mit einem regulären Ausdruck beschrieben und in Ruby-Syntax umsetzen
Reguläre Ausdrücke für XML Bestandteile
 
# ------------------------------
 
def attrListRE
  ws    = %q{\s*}
  nm    = %q{[a-zA-Z][-_:a-zA-Z0-9]*}
 
  dquot = %q{"[^"]*"}
  squot = %q{'[^']*'}
  value = %Q{#{dquot}|#{squot}}
 
  attr  = %Q{(#{nm})#{ws}=#{ws}(#{value})#{ws}}
  Regexp.new(attr)
end
 
$attrListRE = attrListRE
 
# ------------------------------
 
def xmlTokenRE
  ws    = %q{\s*}
  nw    = %q{[^\s]}
  nm    = %q{[a-zA-Z][-_:a-zA-Z0-9]*}
 
  dquot = %q{"[^"]*"}
  squot = %q{'[^']*'}
  value = %Q{#{ws}#{dquot}|#{ws}#{squot}}
 
  attr  = %Q{(?:#{nm}#{ws}(?:=(?:#{value})#{ws})?)}
 
  ec    = %Q{/?>}
  tag   = %Q{</?(?:#{nm})(?:#{ws}(?:#{attr})*)#{ec}}
 
  comment = %q{<!--.*?-->}
  pi      = %Q{<[?](?:#{nm})(?:.|\n)*?>}
  text    = %q{[^<]+}
  dtdDecl = %Q{<!DOCTYPE(?:.|\n)*?>}
  illegal = %q{.}
 
  token   = tag + "|" + comment +
    "|" + pi +
    "|" + text +
    "|" + dtdDecl +
    "|" + illegal
  Regexp.new(token)
end
 
$xmlTokenRE = xmlTokenRE
 
# ------------------------------
 
2. Schritt
Ein einfaches XML Objektmodell
Ruby Klassen für ein Objektmodell für die XML Bestandteile
 
# eine einfache XML Objektstruktur
 
# ------------------------------
 
class XmlText
  def initialize(t)
    @text = t
  end
 
  def XmlText.from_s(t)
    # missing entity and char ref
    # substitution should be added here
    XmlText.new(t)
  end
 
  def to_s
    @text
  end
end
 
# ------------------------------
 
class XmlComment
  def initialize(c)
    @comment = c
  end
 
  def XmlComment.from_s(s)
    t = s.clone
    t[0..3]   = ""
    t[-3..-1] = ""
    XmlComment.new(t)
  end
 
  def to_s
    "<!--" + @comment + "-->"
  end
end
 
# ------------------------------
 
class XmlEndTag
  def initialize(n)
    @name = n
  end
 
  def XmlEndTag.from_s(t)
    t       = t.chop
    t[0..1] = ""
    XmlEndTag.new(t.strip)
  end
 
  def to_s
    "</" + @name + ">"
  end
end
 
# ------------------------------
 
class XmlBeginTag
  def initialize(s,al)
    @name  = s
    @attrl = al
  end
 
  def XmlBeginTag.from_s(s)
    al       = s.chop
    al[0..0] = ""
    n        = al.split[0]
    al[0...n.length] = ""
 
    # missing entity and char ref
    # substitution should be added here
    XmlBeginTag.new(ntokenizeAttrl(al))
  end
 
  def to_s
    "<" + @name + ato_s + ">"
  end
 
  def ato_s
    res = ""
    @attrl.each_pair do |n,v|
      res << " " << n << '="' + v + '"'
    end
    res
  end
end
 
# ------------------------------
 
class XmlEmptyElem
  def XmlEmptyElem.from_s(t)
    t       = t.chop.chop
    t[0..0] = ""
    n       = t.split[0]
    [ XmlBeginTag.from_s("<"  + t + ">"),
      XmlEndTag  .from_s("</" + n + ">")
    ]
  end
end
 
# ------------------------------
 
class XmlPi
  def initialize(n,c)
    @name = n
    @contents = c
  end
 
  def XmlPi.from_s(s)
    n = s.split[0]
    n[0..1] = ""
    c = s[(2 + n.length + 1)..(s.length - 2)]
    XmlPi.new(n,c)
  end
 
  def to_s
    "<?" + @name + " " + @contents + ">"
  end
end
 
# ------------------------------
 
class XmlDTD
  def initialize(d)
    @dtd = d
  end
 
  def XmlDTD.from_s(s)
    c = s[10 .. (s.length - 2)]
    XmlDTD.new(c)
  end
 
  def to_s
    "<!DOCTYPE " + @dtd + ">"
  end
end
 
# ------------------------------
3. Schritt
Ein einfacher Scanner
scannen und Tokens aufbauen
 
load "xmltoken.rb"
load "xmlre.rb"
 
# ------------------------------
 
def tokenizeXml(s)
  s.scan($xmlTokenRE).map do |x|
    mkToken(x)
  end.flatten
end
 
def mkToken(t)
  case
    # comment
  when t[0..2] == "<!-"
    XmlComment.from_s(t)
 
    # PI
  when t[0..1] == "<?"
    XmlPi.from_s(t)
 
    # end tag
  when t[0..1] == "</"
    XmlEndTag.from_s(t)
 
    # doctype decl
  when t[0..8] == "<!DOCTYPE"
    XmlDTD.from_s(t)
 
    # empty element
  when t[0..0] == "<" && t[-2..-1] == "/>"
    n = t.split[0]
    n[0..0] = ""
    XmlEmptyElem.from_s(t)
 
    # begin tag
  when t[0..0] == "<" && t[-1..-1] == ">"
    XmlBeginTag.from_s(t)
 
    # text
  else
    XmlText.from_s(t)
  end
end
 
# ------------------------------
 
def tokenizeAttrl(s)
  al = {}
  s.scan($attrListRE).map do |av|
    k = av[0]
    v = av[1].chop
    v[0..0] = ""
    al[k] = v
  end
  al
end
 
# ------------------------------
4. Schritt
Ein einfacher Scanner
Und ein kleines Testprogramm
 
#!/usr/bin/env ruby
 
require 'net/http'
require 'uri'
 
load 'xml.rb'
 
def getFileContents(fn)
  f = File.open(fn,"r")
  s = f.read
  f.close
  s
end
 
def getHttpContents(uri)
  url = URI.parse(uri)
  req = Net::HTTP::Get.new(url.path)
  res = Net::HTTP.start(url.hosturl.portdo |http|
    http.request(req)
  end
  res.body
end
 
def getContents(uri)
  if uri[0..4] == "http:"
    getHttpContents uri
  else
    getFileContents uri
  end
end
 
def scanXmlPage(fn)
  page = getContents(fn)
  toks = tokenizeXml(page)
end
 
def test0
  scanXmlPage("Hagenbeck.html")
end
 
def test1
  scanXmlPage("Hagenbeck.html").
    map{|xx.to_s}
end
 
def test2
  scanXmlPage("Hagenbeck.html").
    map{|xx.to_s}.
    join
end
 
def test3
  puts(scanXmlPage("Hagenbeck.html").
       map do |x|
         x.to_s
       end.
       join
      )
end
 
if ! ARGV[0].nil? then
  puts(scanXmlPage(ARGV[0]).
       map{|xx.to_s}
      )
end
 

Letzte Änderung: 14.02.2012
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel