XML-Webdienst-aktivierte Office-Dokumente

Veröffentlicht: 16. Dez 2001 | Aktualisiert: 19. Jun 2004
Von Chris Lovett

Links zu verwandten Themen
Download

* * *

Auf dieser Seite

Wie funktioniert dies alles? Wie funktioniert dies alles?
Katalogwebdienst Katalogwebdienst
Schaltfläche "Senden" Schaltfläche "Senden"
Testen Sie's! Testen Sie's!
Nächste Schritte Nächste Schritte

Sind Sie bereit, Microsoft Office XP und .NET Webdienste miteinander zu kombinieren? In einer vernetzten B2B/E-Commerce-Welt ist nichts unmöglich. Warum nicht einfach die leistungsstarken Webdienste mit den Endkunden verbinden, indem Sie den Geschäftsprozessworkflow in alle Aktionen integrieren, die diese Endkunden auf ihren Desktops ausführen? Wovon ich spreche? Das sehen Sie vermutlich am besten, wenn Sie sich das Excel-Tabellenblatt in Abbildung 1 anschauen.

Bild01

Abbildung 1. Webdienst-aktiviertes Excel-Tabellenblatt

Dies ist kein gewöhnliches Tabellenblatt. Es verwendet UDDI (Universal Description, Discovery and Integration), um Firmenadressen zu ermitteln, und einen Katalogwebdienst, um nach Produktinformationen zu suchen. Außerdem wird ein XML-Transform für das XML-Tabellenformat durchgeführt, um ein Purchase Order Request-Formular im Format PIP 3 A4 der Firma RosettaNet zu erzeugen, wenn Sie auf die Schaltfläche Senden klicken.
Wenn Sie den Namen der Firma eingeben, von der Sie einen Artikel erwerben, und dann auf die Schaltfläche Suchen klicken, wird durch VBA-Code im Hintergrund ein UDDI-Aufruf durchgeführt und der übrige Teil des Adressenabschnitts ausgefüllt. Wenn Sie z.B. Microsoft eingeben und auf Suchen klicken, sollte normalerweise Folgendes im Feld Einkauf von angezeigt werden:

Bild02

Abbildung 2. Feld "Einkauf von"

Geben Sie eine Menge (z.B. 23) und das Wort Gummibärchen im Feld Beschreibung ein, und drücken Sie die TAB-TASTE. Durch VBA-Code wird eine Abfrage des SOAP-Katalogwebdienstes durchgeführt, um festzustellen, ob ein übereinstimmender Artikel vorhanden ist. Die Details werden anschließend automatisch ausgefüllt. In diesem Fall habe ich den Katalogwebdienst mit der Nordwind-Datenbank verbunden, erhalte also als Rückgabe Folgendes:

Bild03

Abbildung 3. Detaillierte Betrachtung des Bestellabschnitts der Tabelle

Der Webdienst hat in diesem Fall auch die Beschreibung ausgefüllt und als Hyperlink formatiert, über den Sie zu einer HTML-Seite gelangen, auf der Sie mehr Informationen zu dem Produkt erhalten.
Stimmt mehr als ein Artikel überein und keiner entspricht genau dem, was Sie eingegeben haben, stellt der Webdienst Ihnen eine Dropdownliste bereit, aus der Sie wählen können. Wenn Sie z.B. Tofu eingeben, sehen Sie die folgende Liste:

Bild04

Abbildung 4. Beispiel für eine Multiplechoiceauswahl, wenn keine genaue Übereinstimmung gefunden wird

Wenn Sie einen dieser Artikel auswählen, wird das Feld Beschreibung automatisch mit den entsprechenden Details aufgefüllt.
Nach Angabe aller Informationen klicken Sie auf die Schaltfläche Senden, das RosettaNet PIP 3 A4 XML Purchase Order-Formular wird erzeugt, und die Bestellung wird gesendet.

Wie funktioniert dies alles?

Sie können den der Tabelle zugrunde liegenden VBA-Code durchsuchen, indem Sie auf Extras, Makro und dann auf Visual Basic-Editor klicken. Dem Modul DieseArbeitsmappe liegt Code zugrunde, der auf Änderungen im Tabellenblatt reagiert. Das Workbook_SheetChange-Ereignis löscht einen Posten, wenn Sie die Beschreibung löschen. Das Workbook_SheetSelectionChange-Ereignis ruft die FindProduct()-Methode auf, wenn Sie per TAB aus dem Feld Beschreibung in das Feld LME (LME steht für Liefermengeneinheit) wechseln. Wenn FindProduct einen XMLNode zurückgibt, werden die relevanten Felder aus diesem Knoten gezogen, um die Details für die übrigen Posten aufzufüllen.
Wie der UDDI-find_business-Aufruf funktioniert, können Sie meinem letzten Artikel "UDDI: Ein XML-Webdienst" entnehmen. Wird ein Unternehmen gefunden, werden die addressLines im /businessInfo/contacts/contact/address/-Teil der UDDI-Antwort verwendet, um das Adressfeld Einkauf von aufzufüllen.

Katalogwebdienst

Die FindProduct-Funktion im Katalogmodul ruft den Katalogdienst-URL mit einem URL-Parameter auf, der den nachzuschlagenden Suchbegriff enthält. Die Funktion erwartet eine SOAP-Antwort zurück und überprüft zunächst, ob sie mit /Envelope/Body/Fault übereinstimmt; falls kein Fehler (Fault) vorliegt, bricht sie das Ergebnis der Katalogabfrage (<CatalogQueryResult>) auf, um zu prüfen, ob das ProductName-Attribut in einem der zurückgegebenen Artikel dem Begriff entspricht. Außerdem verschiebt die Funktion die Dropdownliste für die Auswahl weiter nach unten auf die Seite außerhalb des sichtbaren Bereichs. Sie können sich ansehen, wie die Dropdownliste funktioniert, indem Sie auf das Menü Daten klicken und den Befehl Gültigkeit... wählen.
Der Katalogwebdienst ist sehr einfach. Der ASPX-Einstiegspunkt erstellt einfach ein CatalogSearch-Objekt, das in search.cs definiert ist und Execute aufruft (dabei gibt das Objekt einen HttpResponse-Ausgabedatenstrom weiter):

<%@Language="C#" src="search.cs"  Debug="true" %> 
<% 
   Response.ContentType = "text/xml"; 
   string term = Request.QueryString["term"]; 
   if (term != null) { 
      CatalogSearch s = new CatalogSearch(term); 
      s.Execute(output); 
   } else { 
      Response.Write("<Leer/>"); 
   } 
%> 


Die Execute-Methode ist spaßig. Sie besteht aus einem einfachen verwalteten SQL Provider-Code, der in einem XmlTextWriter-Objekt eingeschlossen ist, das die spezifischen Felder aus der SQL SELECT-Anweisung zurückgibt. Im Wesentlichen handelt es sich also um eine While-Schleife im DataReader-Objekt, die Folgendes in das XmlTextWriter-Objekt schreibt:

public void Execute(TextWriter stm) 
{       
   XmlTextWriter xw = new XmlTextWriter(stm); 
   xw.WriteStartElement("Envelope", "http://schemas..../envelope/"); 
   xw.WriteStartElement("Body", "http://schemas..../envelope/"); 
   try { 
      String const = "server=localhost;uid=sa;pwd=;database=nordwind"; 
      SQLConnection con = new SQLConnection(constr); 
      con.Open(); 
      IDataReader reader; 
      String query = "SELECT Artikelname,Einzelpreis,Liefereinheit," + 
           "Lieferanten-Nr,Artikel-Nr FROM Artikel WHERE " + 
           "Artikelname LIKE '%" + term + "%'"; 
      SQLCommand cmd = new SQLCommand(query, con); 
      cmd.Execute(out reader); 
      string funNamespace = "urn:schemas-b2b-fun:catalogs"; 
      xw.WriteStartElement("CatalogQueryResult", funNamespace); 
      while (reader.Read()) 
      { 
         xw.WriteStartElement("Artikel"); 
         xw.WriteAttribute("Artikelname", reader.GetString(0)); 
         xw.WriteAttrDecimal("Einzelpreis", reader.GetDecimal(1)); 
         xw.WriteAttribute("Liefereinheit", reader.GetString(2)); 
         xw.WriteAttribute("LME", "S"+reader.GetInt32(3)+ 
"-P"+reader.GetInt32(4)); 
         xw.WriteEndElement(); 
      } 
      xw.WriteEndElement();  
      con.Close(); 
   } catch (Exception e) { 
      xw.WriteStartElement("Fault"); 
         xw.WriteElementString("faultcode","500"); 
         xw.WriteElementString("faultstring",e.ToString()); 
      xw.WriteEndElement(); 
   } 
   xw.WriteEndElement(); 
   xw.WriteEndElement(); 
   xw.Close(); 
} 


Der URL http://localhost/catalog/search.aspx?term=Tofu liefert das folgende Ergebnis:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> 
<Body> 
  <CatalogQueryResult xmlns="urn:schemas-b2b-fun:catalogs"> 
    <item Artikelname="Tofu" Einzelpreis="23,25"  
 Liefereinheit="40 - 100-g-Packungen." LME="S6-P14"/> 
    <item Artikelname="Räucher-Tofu" Einzelpreis="10"  
 Liefereinheit="5 kg-Packung." LME="S4-P74"/> 
  </CatalogQueryResult> 
</Body> 
</Envelope> 


Auf diese Weise können Sie am effizientesten XML-Code aus SQL Server per .NET Framework abrufen. Bei sehr groben Messungen erhielt ich ca. 80 bis 90 dieser Ergebnisse pro Sekunde auf meiner Dell PowerEdge 2400-Arbeitsstation.

Schaltfläche "Senden"

Die SendOrder()-Funktion lädt ein XML-Dokument aus einer XML-Darstellung eines ausgewählten Zellbereichs im Tabellenblatt. Dies wird – Hokuspokus – über die folgenden Zeilen VBA-Code erreicht:

With ActiveSheet  
  Set sourcexml = New MSXML2.DOMDocument 
  sourcexml.loadXML .Range("B1:N34").value(xlRangeValueXMLSpreadsheet) 
End With 


Diese Codeanweisung hat als Rückgabe einen riesigen XML-Abschnitt, der jedes Detail in diesem Zellbereich der Tabelle beschreibt. Der folgende Codeausschnitt wurde diesem XML-Abschnitt entnommen:

<Workbook> 
    <Worksheet> 
        <Table> 
            <Row> 
                <Cell ss:StyleID="s23"><Data ss:Type="Number">23</Data> 
<NamedCell ss:Name="Artikel"/></Cell> 
 <Cell ss:MergeAcross="4" ss:StyleID="m31209522"  
ss:HRef="http://eshop.msn.com/category.asp?catId=170"> 
<Data ss:Type="String">Gumbär Gummibärchen</Data></Cell> 
<Cell ss:StyleID="s52"> 
<Data ss:Type="String">S3-P7</Data></Cell> 
                <Cell ss:StyleID="s26"> 
<Data ss:Type="Number">30</Data> 
<NamedCell ss:Name="Einzelpreis"/></Cell> 
                <Cell ss:StyleID="s27"> 
<Data ss:Type="String">100 - 250-g-Beutel.</Data></Cell> 
<Cell ss:StyleID="s37"> 
<Data ss:Type="Number">690</Data></Cell> 
                <Cell ss:StyleID="s49"/> 
            </Row> 
        </Table> 
    </Worksheet> 
</Workbook> 


Wir verwenden dann ein XSL-Stylesheet, um daraus das folgende Format abzuleiten:

<PurchaseOrder xmlns="http://www.rosettanet.org"> 
   <deliverTo> 
      <PhysicalAddress> 
         <cityName>60439 Frankfurt</cityName> 
         <addressLine1>Plutzer Lebensmittelgroßmärkte AG</addressLine1> 
         <addressLine2>Bogenallee 51</addressLine2> 
         <regionName>Deutschland</regionName> 
      </PhysicalAddress> 
   </deliverTo> 
   <ProductLineItem> 
      <ProductQuantity>23</ProductQuantity> 
      <productUnit> 
         <ProductPackageDescription> 
            <ProductIdentification> 
               <GlobalProductIdentifier>S3-P7</GlobalProductIdentifier> 
            </ProductIdentification> 
         </ProductPackageDescription> 
      </productUnit> 
      <Description>Gumbär Gummibärchen</Description> 
      <requestedPrice> 
         <FinancialAmount> 
            <GlobalCurrencyCode>DM</GlobalCurrencyCode> 
            <MonetaryAmount>30</MonetaryAmount> 
         </FinancialAmount> 
      </requestedPrice> 
   </ProductLineItem> 
   <thisDocumentGenerationDateTime> 
      <DateTimeStamp>15-03-2001T00:00:00.000</DateTimeStamp> 
   </thisDocumentGenerationDateTime> 
</PurchaseOrder> 


Anmerkung Dies ist vermutlich kein technisch vollständiger Request gemäß der RosettaNet PIP 3 A4 Purchase Order Request-Spezifikation, aber Sie erhalten eine Vorstellung davon.
Damit diese Transformation einigermaßen stabil ist, wenden wir einen Trick an: Wir benennen die wichtigen Zellen, aus denen wir die Daten exportieren möchten. Dies erfolgt mit der folgenden Formatvorlage eines XPath-Ausdrucks im XSLT-Transform:

 select="/Workbook/Worksheet/Table/Row/Cell[NamedCell[@ss:Name='City']] 


Dieser besondere Ausdruck findet die Zelle (Cell) mit dem Namen (Named) City. Der restliche Teil des Stylesheets ist ziemlich einfach. Weitere Informationen finden Sie unter XLToPO.xsl.

Testen Sie's!

Wenn Sie dieses Beispiel testen möchten, brauchen Sie einfach nur MSXML 3.0 zu installieren und sich eine Kopie der Nordwind-Datenbank zu besorgen. Der Democode ist folgendermaßen mit SQL Server verknüpft:

   SQLConnection("server=localhost;uid=sa;pwd=;database=nordwind");


Gegebenenfalls müssen Sie diesen Codeabschnitt anpassen, wenn sich die Nordwind-Datenbank an einem anderen Speicherort befindet.
Im Tabellenblatt von PO.xsl wird folgender Speicherort des Katalogdienstes angenommen:
http://localhost/catalog/search.aspx

Sie müssen die search.aspx, search.cs und XLToPO.xsl des Webdienstes in einem virtuellen Verzeichnis namens catalog auf Ihrem lokalen Computer installieren oder den Verweis im Tabellenblatt auf einen anderen Speicherort zeigen lassen.
Damit das Tabellenblatt bearbeitet werden kann, müssen Sie den Schutz deaktivieren (dazu verwenden Sie das Untermenü Extras | Schutz).

Nächste Schritte

Ideal wäre es, wenn Sie die Katalogdienstbindungen des Lieferanten in UDDI speichern. Mit einigen VBA-Codeanweisungen, die auskommentiert sind, erzielen Sie dies im Handumdrehen. Diese Anweisungen suchen nach anerkannten Katalogdienstinformationen (serviceInfo nach serviceKey) und verwenden bei Erfolg den Einstiegspunkt (accessPoint), der in den Dienstdetails (serviceDetails) enthalten ist. Die Pseudokatalog-API, die ich in dieser Demo verwende, ist nicht als bekannter Diensttyp in UDDI registriert.
Es würde Spaß machen, diese Übung mit dem Office-Feature SmartTags auf ähnliche Weise durchzuführen. Weitere Informationen finden Sie im SmartTags SDK unter http://msdn2.microsoft.com/en-us/office/aa905340.aspx (in Englisch).


Anzeigen: