#!c:\Perl\bin\perl.exe
#!/usr/bin/perl
# fmp2perl.pm
package FMPro::fmp2perl;
use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
require Exporter;
@ISA = qw(Exporter AutoLoader);
@EXPORT = qw();
@EXPORT_OK = qw(fmp2perl);
$VERSION = '1.1.03';
##################################################################
# fmp2perl
#
# Parst FMPro XML Ausgabe; verarbeitet nur UTF-8 XML
# Es wird Konvertierung nach latin1 vorgenommen
#
# input: $String oder
# "http://www.domain.de/FMPro?-DB=Datenbank&-format=-dso_xml&-Find=" oder
# "http://www.domain.de/FMPro?-DB=Datenbank&-format=-fmp_xml&-Find=" oder
# "c:\\Inetpub\\wwwroot\\Baurat\\tmp\\Datei.xml"
#
# output:Referenz $xml oder "Error - Errortext"
# z.B.
# $xml->[3]{Kurzbezeichnung}{Data}[0] ==> Kurzbezeichnung des 3. Treffer-DS
#
##################################################################
sub fmp2perl
{
use LWP::Simple; # get, getstore
my ($input) = @_;
my ($tmp,$i,$j,$String,$xml,$switch);
BEGIN
{
unless (eval "use Unicode::String qw(latin1 utf8)")
{
$switch = "noUnicode";
}
else
{
Unicode::String->stringify_as('utf8');
$switch = "okUnicode";
}
}
##################################################################
### Prfen, ob $input Datei ist. Wenn ja auslesen.
if (-f $input)
{
$String = &GET_DATEI_STRING($input);
unless ($String =~ m,encoding="UTF-8",im)
{
return("Error - Die angegebene Datei $input liegt nicht im Format UTF-8 vor.");
}
unless($String =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?><(FMP[DX][SM][OL]RESULT) xmlns="http://www.filemaker.com/fmp[dx][sm][ol]result">,m && $String =~ m,,m)
{
return("Error - Die angegebene Datei $input liegt nicht im FileMaker XML-Format (-dso_xml oder -fmp_xml) vor.");
}
if($1 eq "FMPDSORESULT")
{
$xml->[0]{CurrentFormat} = "-dso_xml";
}
else
{
$xml->[0]{CurrentFormat} = "-fmp_xml";
}
}
### Prfen, ob $input URL ist. Wenn ja anfordern.
# z.B. http://domain.de?-db=BauBuch.fp5&-lay=WWW_BR_NavRe&-Format=-dso_xml&-Skip=10&-Find=
elsif ($input =~ m,^http://,)
{
unless ($input =~ m,-format=-dso_xml,im | $input =~ m,-format=-fmp_xml,im)
{
return("Error - Die angegebene URL liefert kein dso_xml oder fmp_xml zurück. Bitte \"-format=-dso_xml\" angeben!");
}
$String = get("$input"); # ber String kann man den ordnungsgemen Ablauf kontrollieren
unless ($String =~ m,encoding="UTF-8",im)
{
return("Error - Das unter $input zurückgelieferte XML liegt nicht im Format UTF-8 vor.");
}
$xml->[0]{URL} = $input;
$input =~ m,&-format=(-[fd][ms][po]_xml)&,im;
$xml->[0]{CurrentFormat} = $1;
if($input =~ m,&-skip=(\d*?)&,im){$xml->[0]{CurrentSkip} = $1;}else{$xml->[0]{CurrentSkip} = 0}
if($input =~ m,&-max=(\d*?)&,im){$xml->[0]{CurrentMax} = $1;}else{$xml->[0]{CurrentMax} = 25;}
if($input =~ m,&-lop=(\w*?)&,im){$xml->[0]{CurrentLOP} = $1;}else{$xml->[0]{CurrentLOP} = "and"}
while($input =~ m,&,gm){$tmp = pos($input);}
$xml->[0]{CurrentAction} = substr($input,$tmp);
# Sortierfolge z.B.: &-SortField=News_Dat&-SortOrder=Descend&-SortField=News_Zei&-SortOrder=Descend&
if($input =~ m,&(-sortfield=[\w\W\s\S\d\D]*&-sortorder=[ascend|descend|ascending|descending|custom=][\w\W\s\S\d\D]*?)&,im){$xml->[0]{CurrentSort} = $1;}else{$xml->[0]{CurrentSort} = ""}
}
### Wenn FMPro DSO_XML/FMP_XML $input selbst parsen
elsif ($input =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?><(FMP[DX][SM][OL]RESULT) xmlns="http://www.filemaker.com/fmp[dx][sm][ol]result">,m && $input =~ m,,m)
{
$String = $input;
unless ($String =~ m,encoding="UTF-8",im)
{
return("Error - Das übergebene XML liegt nicht im Format UTF-8 vor.");
}
if($1 eq "FMPDSORESULT")
{
$xml->[0]{CurrentFormat} = "-dso_xml";
}
else
{
$xml->[0]{CurrentFormat} = "-fmp_xml";
}
}
### Fehlermeldung
else
{
return("Error - Der übergebene String ist weder eine Pfadangabe, noch eine URL oder FMPro DSO_XML.");
}
# Konvertierung von UTF-8 nach Latin1
if($switch eq "okUnicode")
{
$String = new Unicode::String($String);
$String = $String->latin1();
}
else
{
$String = UTF8_LATIN1($String);
}
$String =~ s/\n/
/mg;
### Parsing FMPDSORESULT
if ($xml->[0]{CurrentFormat} eq "-dso_xml")
{
my (@tmp,@tmp1,@tmp2,$k);
# Werte auslesen, inclusive
# CurrentRecID, CurrentModID, CurrentLayout, CurrentDatabase, CurrentError
if($String =~ m,(\d*?),mg){$xml->[0]{CurrentError} = $1;}
if($String =~ m,([\w\W\s\S\d\D]*?),mg){$xml->[0]{CurrentDatabase} = $1;}
if($String =~ m,([\w\W\s\S\d\D]*?),mg){$xml->[0]{CurrentLayout} = $1;}
@tmp = $String =~ m,([\w\W\s\S\d\D]*?)
,mg;
$xml->[0]{CurrentOutCount} = (scalar @tmp)/3;
for ($i=0; $i<@tmp; $i+=3)
{
$xml->[$i/3]{CurrentModID} = $tmp[$i];
$xml->[$i/3]{CurrentRecID} = $tmp[$i+1];
$xml->[$i/3]{CurrentRecordNumber} = $i/3+1;
@tmp1 = $tmp[$i+2] =~ m,<(?:DATA){0}([\w\W\s\S\d\D]*?)>([\w\W\s\S\d\D]*?)(?:DATA){0}\1>,mg; # \1 ist wie $1, kann aber schon wrend des Suchvorganges benutzt werden
for ($j=0; $j<@tmp1; $j+=2)
{
if(@tmp2 = $tmp1[$j+1] =~ m,([\w\W\s\S\d\D]*?),mg)
{
# Wiederholfeld
for($k=0;$k<@tmp2;$k++)
{
$xml->[$i/3]{$tmp1[$j]}{Data}[$k] = $tmp2[$k];
}
}
else
{
$xml->[$i/3]{$tmp1[$j]}{Data}[0] = $tmp1[$j+1];
}
}
}
}
### Parsing FMPXMLRESULT oder FMPXMLLAYOUT
elsif ($xml->[0]{CurrentFormat} eq "-fmp_xml")
{
my ($tmp,@tmp,@tmp1,@tmp2,$i,$j,$k,$m,@FeldName);
# 0
if($String =~ m,(\d*?),mg)
{
$xml->[0]{CurrentError} = $1;
}
#
if($String =~ m,,m)
{
$xml->[0]{ProductBuild} = $1;
$xml->[0]{ProductName} = $2;
$xml->[0]{ProductVersion} = $3;
}
### Parsing FMPXMLRESULT
#
if($String =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?>,m && $String =~ m,,m)
{
if($String =~ m,,mg)
{
$xml->[0]{DateFormat} = $1;
$xml->[0]{CurrentLayout} = $2;
$xml->[0]{CurrentDatabase} = $3;
$xml->[0]{CurrentRecordCount} = $4;
$xml->[0]{TimeFormat} = $5;
}
if($String =~ m,([\w\W\s\S\d\D]*?),m)
{
# $tmp[0]=EmptyOk $tmp[1]=MaxReapeat $tmp[2]=Name $tmp[3]=Type $tmp[4]=EmptyOk ...
@tmp = $1 =~ m,,mg;
for ($i=0; $i<@tmp; $i+=4)
{
$xml->[0]{$tmp[$i+2]}{EmptyOk} = $tmp[$i];
$xml->[0]{$tmp[$i+2]}{MaxReapeat} = $tmp[$i+1];
$xml->[0]{$tmp[$i+2]}{Type} = $tmp[$i+3];
$FeldName[$i/4] = $tmp[$i+2]; # Feldnamen fr nchste Schleife
}
}
if($String =~ m,([\w\W\s\S\d\D]*?),m)
{
$xml->[0]{CurrentFoundCount} = $1;
@tmp = $2 =~ m,([\w\W\s\S\d\D]*?)
,mg;
$xml->[0]{CurrentOutCount} = (scalar @tmp)/3;
for ($j=0; $j<@tmp; $j+=3)
{
$xml->[$j/3]{CurrentModID} = $tmp[$j];
$xml->[$j/3]{CurrentRecID} = $tmp[$j+1];
$xml->[$j/3]{CurrentRecordNumber} = $j/3+1;
if(@tmp1 = $tmp[$j+2] =~ m,
([\w\W\s\S\d\D]*?),mg) # Kein Wiederholfeld
{
for($k=0;$k<@tmp1;$k++)
{
if($tmp1[$k] =~ m,,m)
{
# Wiederholfeld mit mindestens 2 Wiederholungen
@tmp2 = split(/<\/DATA>/,$tmp1[$k]);
for($m=0;$m<@tmp2;$m++)
{
$xml->[$j/3]{$FeldName[$k]}{Data}[$m] = $tmp2[$m];
}
}
else
{
$xml->[$j/3]{$FeldName[$k]}{Data}[0] = $tmp1[$k];
}
}
}
}
}
}
### Parsing FMPXMLLAYOUT
elsif($String =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?>,m && $String =~ m,,m)
{
if($String =~ m,,mg)
{
$xml->[0]{CurrentDatabase} = $1;
$xml->[0]{CurrentLayout} = $2;
}
# 1024x1536..
@tmp1 = $String =~ m,([\w\W\s\S\d\D]*?),mg;
for ($i=0; $i<@tmp1; $i+=2)
{
# 1024x1536
@tmp2 = $tmp1[$i+1] =~ m,([\w\W\s\S\d\D]*?),mg;
for ($j=0; $j<@tmp2; $j++)
{
$xml->[0]{ValueList}{$tmp1[$i]}[$j] = $tmp2[$j];
}
}
}
}
return($xml);
}
##################################################################
# GET_DATEI_STRING (siehe TEMPLATE_LESEN)
# Liest Datei als String ein (Schlrfmodus)
#
# input: absoluter Pfad zur Datei
#
# output: Inhalt der Datei
##################################################################
sub GET_DATEI_STRING
{
my $Datei=$_[0];
my $DateiInhalt = '';
my $FH = "FILE".int(rand()*10000);
if (-e $Datei)
{
open ($FH, "< $Datei") || die "Kann Datei $Datei nicht oeffen: $!\n";
local $/; # Schlrfmodus einschlaten
while (<$FH>)
{
$DateiInhalt = $_; # gesamte Datei drin!
}
close ($FH) || die "Can't close the text file: $!\n";
}
else {die "Datei $Datei kann nicht geoeffnet werden";}
return ($DateiInhalt);
}
##################################################################
# UTF8_LATIN1
# Nicht schn und intelligent, dafr geht es auch ohne Modul
#
# input: $string utf8-codiert
#
# output: $string latin1-codiert
#
##################################################################
sub UTF8_LATIN1
{
#my $string = $_[0];
# Zerlegt einen String in einzelne Zeichen
#my @chars = unpack("U" x length($_[0]), $_[0]); # "U" unicode
#my @chars = unpack("a1" x length($_[0]), $_[0]);
#my ($char);
#$_[0] =~ tr/\0-\x{ff}//UC; # utf8 to latin1 char
# Fr Perl 5.8
#use Encode;
#$octets = encode("iso-8859-15", $utf8string);
$_[0] =~ s/“|”/"/mg; # bertragung von 3-Byte-Zeichen
$_[0] =~ s/([])/$1/mg;
$_[0] =~ s/Ñ//mg;
$_[0] =~ s/Ò//mg;
$_[0] =~ s/á//mg;
$_[0] =~ s/â//mg;
$_[0] =~ s/ã//mg;
$_[0] =~ s/ä//mg;
$_[0] =~ s/å//mg;
$_[0] =~ s/æ//mg;
$_[0] =~ s/ç//mg;
$_[0] =~ s/è//mg;
$_[0] =~ s/é//mg;
$_[0] =~ s/ê//mg;
$_[0] =~ s/ë//mg;
$_[0] =~ s/ì//mg;
$_[0] =~ s/í//mg;
$_[0] =~ s/î//mg;
$_[0] =~ s/ï//mg;
$_[0] =~ s/ð//mg;
$_[0] =~ s/ñ//mg;
$_[0] =~ s/ò//mg;
$_[0] =~ s/ó//mg;
$_[0] =~ s/ô//mg;
$_[0] =~ s/õ//mg;
$_[0] =~ s/ö//mg;
$_[0] =~ s/÷//mg;
$_[0] =~ s/ø//mg;
$_[0] =~ s/ù//mg;
$_[0] =~ s/ú//mg;
$_[0] =~ s/û//mg;
$_[0] =~ s/ü//mg;
$_[0] =~ s/ý//mg;
$_[0] =~ s/þ//mg;
$_[0] =~ s/ÿ//mg;
$_[0] =~ s/ //mg;
$_[0] =~ s/À//mg;
$_[0] =~ s/Á//mg;
$_[0] =~ s/Ä//mg;
$_[0] =~ s/Å//mg;
$_[0] =~ s/Æ//mg;
$_[0] =~ s/Ç//mg;
$_[0] =~ s/È//mg;
$_[0] =~ s/É//mg;
$_[0] =~ s/Ê//mg;
$_[0] =~ s/Ë//mg;
$_[0] =~ s/Ì//mg;
$_[0] =~ s/Í//mg;
$_[0] =~ s/Î//mg;
$_[0] =~ s/Ï//mg;
$_[0] =~ s/Ð//mg;
$_[0] =~ s/Ó//mg;
$_[0] =~ s/Ô//mg;
$_[0] =~ s/Õ//mg;
$_[0] =~ s/Ö//mg;
$_[0] =~ s/×//mg;
$_[0] =~ s/Ø//mg;
$_[0] =~ s/Ù//mg;
$_[0] =~ s/Ú//mg;
$_[0] =~ s/Û//mg;
$_[0] =~ s/Ü//mg;
$_[0] =~ s/Ý//mg;
$_[0] =~ s/Þ//mg;
$_[0] =~ s/ß//mg;
$_[0] =~ s/Ã//mg;
$_[0] =~ s/Â//mg;
return ($_[0]);
}
1;
__END__
=head1 NAME
FMPro::fmp2perl - Parst FMPro-XML und gibt Feldnamen, -werte und
Requestparameter als Referenz zurEck.
=head1 SYNOPSIS
use FMPro::fmp2perl qw(fmp2perl);
$tmp = ; oder ;
$values = fmp2perl($tmp);
$title = $values->[0]{title}{Data}[0];
$database = $values->[0]{CurrentDatabase};
$RecID = $values->[0]{CurrentRecID};
$type = $values->[0]{title}{Type}; # only for FMPXMLRESULT
=head1 DESCRIPTION
fmp2perl parst FMPro-XML und gibt Feldnamen, -werte und Requestparameter als Referenz
zurEck. Das XML kann als FMPDSORESULT oder FMPXMLRESULT Ebergeben werden.
fmp2perl ist auf UNIX- und WINDOWS-Systemen lauffEhig.
Bisher nur unter ActivePerl 5.8 getestet.
=head2 Input
=over 4
=item B
enthElt eine XML-Struktur der Form FMPBRESULT oder FMPBRESULT.
Es wird nur UTF-8 Kodierung berEcksichtigt.
=item B
enthElt eine XML-Struktur der Form FMPBRESULT oder FMPBRESULT.
Es wird nur UTF-8 Kodierung berEcksichtigt.
=item B
enthElt einen FMPro-Datenbank-Request.
=back
=head2 Output
ErlEuterungen zur Output-Tabelle:
I<$v = fmp2perl($tmp)>
X/U - $tmp ist URL im FMPXMLRESULT Format
X/FS - $tmp ist Pfad zu File oder String ~
D/U - $tmp ist URL im FMPDSORESULT Format
D/FS - $tmp ist Pfad zu File oder String ~
Wert - z.B. {}{Data}[0]......$v->[0]{}{Data}[0]
mit
$v ->[0]- 1. Datensatz
{}- Name des Feldes
{Data}- Ausgabe des Feldinhaltes
[0]- 0. Wiederholung eines Wiederholfeldes bzw. Feld ohne Wiederholung
B
x x {CurrentFoundCount}
x x x x {}{Data}[0..-1]
x x {}{EmptyOk}
x x {}{MaxReapeat}
x x {}{Type}
x x x x {CurrentFormat}
x x {CurrentAction}
x x x x {CurrentDatabase}
x x x x {CurrentError}
x x x x {CurrentLayout}
x x {CurrentLOP}
x x {CurrentMax}
x x x x {CurrentModID}
x x x x {CurrentOutCount}
x x {CurrentRecordCount}
x x {CurrentSkip}
x x {CurrentSort}
x x x x {CurrentRecID}
x x x x {CurrentRecordNumber}
x x {DateFormat}
x x {ProductBuild}
x x {ProductName}
x x {ProductVersion}
x x {TimeFormat}
x x {URL}
Die Variablennamen wurden in Anlehnung an die CDML-Syntax vergeben.
Daher wird die Lektuere der CDML-Referenz zur Bedeutung der Werte empfohlen.
Einzige Ausnahme ist der Wert {CurrentOutCount}
( also z.B. $v->[2]{CurrentOutCount} ).
{CurrentOutCount} - Anzahl der Datensaetze in der XML-Struktur; nur bei FMPBRESULT
=head1 EXAMPLE
use FMPro::fmp2perl qw(fmp2perl);
# Beispiel mit URL FMPDSORESULT
$tmp = "http://www.domain.com/FMPro?-db=your-database.fp5";
$tmp .= "&-lay=your-layout&-Format=-dso_xml&-FindAll=";
$values = fmp2perl($tmp);
# Beispiel mit URL FMPXMLRESULT
$tmp = "http://www.domain.com/FMPro?-db=your-database.fp5";
$tmp .= "&-lay=your-layout&-Format=-fmp_xml&-FindAll=";
$values = fmp2perl($tmp);
# Beipiel mit Angabe eines Files (UNIX)
$tmp = "/xml_data/path/fmp_xml_file.xml";
$values = fmp2perl($tmp);
# Beipiel mit Angabe eines Files (WINDOWS)
$tmp = "C:\\Inetpub\\wwwroot\\xml_data_path\\fmp_xml_file.xml";
$values = fmp2perl($tmp);
# Beispiel mit Uebergabe einer FMPBRESULT XML-Struktur
$tmp =<<__XML__;
0
YOUR-DATABASE.FP5
your title
__XML__
$values = fmp2perl($tmp);
# Beispiel mit Uebergabe einer FMPBRESULT XML-Struktur
$tmp =<<__XML__;
0
your title
__XML__
$values = fmp2perl($tmp);
$title = $values->[0]{title}{Data}[0];
$database = $values->[0]{CurrentDatabase};
$RecID = $values->[0]{CurrentRecID};
$type = $values->[0]{title}{Type}; # only for FMPXMLRESULT
=head1 BUGS
=head1 AUTHOR
Hans-Martin Aurich info@webconsultant.de technik@baurat.de
=head1 COPYRIGHT
Copyright 2004 - 2005 Hans-Martin Aurich. All rights reserved.
=head1 SEE ALSO
=cut