2012-01-04 11 views
12

Tło:Ignorując nazw dostarczonych podczas walidacji XML z XSD

Budujemy aplikację, która pozwala naszym klientom dostarczać danych w predefiniowanym (tj. Nie kontrolujemy) formacie XML. XSD jest dostarczany nam przez firmę zewnętrzną i oczekujemy, że otrzymamy plik XML, który przejdzie sprawdzanie schematu przed jego przetworzeniem.

Problem:

XSD, które są dostarczane wraz zawiera domyślną i docelowej przestrzeni nazw, co oznacza, że ​​jeśli klient dostarcza plik XML, który nie zawiera nazw, a następnie przejdzie walidacji . Oczywiście nie chcemy, aby dostarczali rzeczy, które mówią, że przechodzą, ale nie powinni, ale większy niepokój wiąże się z masą dodatkowych kontroli, które będziemy musieli wykonać na każdym elemencie, jeśli nie będę w stanie znaleźć rozwiązania. sprawdzanie poprawności XML.

na pytania:

Czy to możliwe, aby wymusić .NET do sprawdzania poprawności i ignorować nazw na dołączonej XML i XSD. tj. w pewien sposób "założyć", że obszar nazw został dołączony.

  1. Czy istnieje możliwość usunięcia przestrzeni nazw w pamięci, łatwo i niezawodnie?
  2. Jaka jest najlepsza praktyka w takich sytuacjach?

Solutions że mam tak daleko:

  1. Usuń z nazw XSD za każdym razem to zaktualizowana (nie powinny być bardzo często nie ominąć faktu, że jeśli oni. Podaj przestrzeń nazw, która będzie nadal przebiegać sprawdzanie poprawności
  2. Usuń przestrzeń nazw z XSD, I znaleźć sposób, aby pozbawić przestrzeni nazw z przychodzących XML za każdym razem.To wygląda jak wiele kodu, aby wykonać coś prostego.Narzędzie
  3. Doe s jakieś wstępne kwalifikacje do pliku XML, zanim zostaną sprawdzone, aby upewnić się, że ma poprawną przestrzeń nazw. Wydaje się, że nie udało się ich zawiodć z powodu nieprawidłowej przestrzeni nazw, jeśli zawartość pliku jest poprawna.
  4. Utwórz duplikat XSD, który nie ma przestrzeni nazw, ale jeśli po prostu podadzą niewłaściwy obszar nazw lub inną przestrzeń nazw, to nadal będzie przekazywać.

Przykład XML:

<?xml version="1.0"?> 
<xsd:schema version='3.09' elementFormDefault='qualified' attributeFormDefault='unqualified' id='blah' targetNamespace='urn:schemas-blah.com:blahExample' xmlns='urn:blah:blahExample' xmlns:xsd='http://www.w3.org/2001/XMLSchema'> 
... 
</xsd:schema> 

z nazw, które różni

<?xml version="1.0" encoding="UTF-8" ?> 
<root xmlns="urn:myCompany.com:blahExample1" attr1="2001-03-03" attr2="google" > 
... 
</root> 

bez nazw wcale.

<?xml version="1.0" encoding="UTF-8" ?> 
<root attr1="2001-03-03" attr2="google" > 
... 
</root> 
+0

Przestrzenie nazw XML to dobra rzecz, po co z tym walczyć? –

+1

jest to coś, nad czym nie możemy zapanować, chcę się upewnić, że klienci wysyłają poprawny XML, jednak jeśli klient pominie deklarację przestrzeni nazw w swoim przesłanym pliku XML, chciałbym powiedzieć, że nadal możemy ją zweryfikować. Nie chcę po prostu powiedzieć "zawiedliście, teraz napraw to!" (i tak, użyłbym lepszych słów, ale masz pomysł). – Martin

Odpowiedz

6

Próbując rozwiązać ten sam problem. Wpadłem na to, co uważam za dość czyste rozwiązanie. Dla jasności pominąłem pewne sprawdzanie parametrów wejściowych.

Najpierw scenariusz: Jest to usługa, która odbiera plik, który ma być „dobrze uformowane” XML i ważny przeciwko XSD. Oczywiście nie mamy zaufania do "dobrej fomrmness" ani, że jest ona ważna w stosunku do XSD, że "wiemy" jest poprawne.

Kod dla takiej metody usługi sieciowej przedstawiono poniżej, myślę, że to nie wymaga objaśnień.

Głównym celem jest kolejność, w której odbywa się walidacja, nie sprawdza się obszaru nazw przed załadowaniem, można sprawdzić po, ale czyściutko.

Zdecydowałem, że mogę żyć z pewną obsługą wyjątków, ponieważ oczekuje się, że większość plików będzie "dobra" i ponieważ jest to ramowy sposób postępowania (więc nie będę z tym walczyć).

private DataTable xmlErrors; 
[WebMethod] 
public string Upload(byte[] f, string fileName) { 
    string ret = "This will have the response"; 

    // this is the namespace that we want to use 
    string xmlNs = "http://mydomain.com/ns/upload.xsd"; 

    // you could put a public url of xsd instead of a local file 
    string xsdFileName = Server.MapPath("~") + "//" +"shiporder.xsd"; 

    // a simple table to store the eventual errors 
    // (more advanced ways possibly exist) 
    xmlErrors = new DataTable("XmlErrors"); 
    xmlErrors.Columns.Add("Type"); 
    xmlErrors.Columns.Add("Message"); 

    try { 
     XmlDocument doc = new XmlDocument(); // create a document 

     // bind the document, namespace and xsd 
     doc.Schemas.Add(xmlNs, xsdFileName); 

     // if we wanted to validate if the XSD has itself XML errors 
     // doc.Schemas.ValidationEventHandler += 
     // new ValidationEventHandler(Schemas_ValidationEventHandler); 

     // Declare the handler that will run on each error found 
     ValidationEventHandler xmlValidator = 
      new ValidationEventHandler(Xml_ValidationEventHandler); 

     // load the document 
     // will trhow XML.Exception if document is not "well formed" 
     doc.Load(new MemoryStream(f)); 

     // Check if the required namespace is present 
     if (doc.DocumentElement.NamespaceURI == xmlNs) { 

      // Validate against xsd 
      // will call Xml_ValidationEventHandler on each error found 
      doc.Validate(xmlValidator); 

      if (xmlErrors.Rows.Count == 0) { 
       ret = "OK"; 
      } else { 
       // return the complete error list, this is just to proove it works 
       ret = "File has " + xmlErrors.Rows.Count + " xml errors "; 
       ret += "when validated against our XSD."; 
      } 
     } else { 
      ret = "The xml document has incorrect or no namespace.";     
     } 
    } catch (XmlException ex) { 
     ret = "XML Exception: probably xml not well formed... "; 
     ret += "Message = " + ex.Message.ToString(); 
    } catch (Exception ex) { 
     ret = "Exception: probably not XML related... " 
     ret += "Message = " + ex.Message.ToString(); 
    } 
    return ret; 
} 

private void Xml_ValidationEventHandler(object sender, ValidationEventArgs e) { 
    xmlErrors.Rows.Add(new object[] { e.Severity, e.Message }); 
} 

Teraz xsd musiałby somthing jak:

<?xml version="1.0" encoding="utf-8"?> 
<xs:schema id="shiporder" 
    targetNamespace="http://mydomain.com/ns/upload.xsd" 
    elementFormDefault="qualified" 
    xmlns="http://mydomain.com/ns/upload.xsd" 
    xmlns:mstns="http://mydomain.com/ns/upload.xsd" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
> 
    <xs:simpleType name="stringtype"> 
     <xs:restriction base="xs:string"/> 
    </xs:simpleType> 
    ... 
    </xs:schema> 

i "dobre" XML byłoby coś jak:

<?xml version="1.0" encoding="utf-8" ?> 
<shiporder orderid="889923" xmlns="http://mydomain.com/ns/upload.xsd"> 
    <orderperson>John Smith</orderperson> 
    <shipto> 
    <names>Ola Nordmann</names> 
    <address>Langgt 23</address> 

testowałem, "złe formacie XML", "nieprawidłowe dane wejściowe zgodnie z XSD", "nieprawidłowa przestrzeń nazw".

odniesienia:

Read from memorystream

Trying avoid exception handling checking for wellformness

Validating against XSD, catch the errors

Interesting post about inline schema validation


Hi Martin, komentarz sction jest zbyt krótki na moją odpowiedź, więc dam to tutaj, to może być lub nie być być kompletna odpowiedź, niech go poprawi razem :)

Zrobiłem następujące testy :

  • test: xmlns = "Blaa"
  • Wynik: plik zostanie odrzucona z powodu złej nazw.
  • Test: xmlns = "http://mydomain.com/ns/upload.xsd" i xmlns: a = "blaa", a elementy miały "a: someElement"
  • Wynik: Błąd pliku retreatowego, który powiedział, że jest nie oczekując "a: someElement"
  • Test: xmlns = "http://mydomain.com/ns/upload.xsd" i xmlns: a = "blaa" i elementy miały "someElement" z brakującym wymaganym atrybutem
  • Wynik: błąd wraca plików mówiąc, że atrybut brakuje

strategia następnie (wich wolę) było, jeśli d ocument nie jest zgodny, a następnie nie akceptuje, ale podaje pewne informacje z powodu (np. "niewłaściwy obszar nazw").

Strategia ta wydaje się wbrew temu, co wcześniej powiedział:

jednak, jeśli klient nie zdobywa się deklarację przestrzeni nazw XML w ich przekazywanych następnie chciałbym powiedzieć, że możemy jeszcze potwierdzić. Nie chcę po prostu powiedzieć "zawiedliście, teraz napraw to!"

W tym przypadku wydaje się, że można po prostu zignorować zdefiniowaną przestrzeń nazw w pliku XML. Aby to zrobić, że można pominąć sprawdzanie prawidłowego nazw:

... 
    // Don't Check if the required namespace is present 
    //if (doc.DocumentElement.NamespaceURI == xmlNs) { 

     // Validate against xsd 
     // will call Xml_ValidationEventHandler on each error found 
     doc.Validate(xmlValidator); 

     if (xmlErrors.Rows.Count == 0) { 
      ret = "OK - is valid against our XSD"; 
     } else { 
      // return the complete error list, this is just to proove it works 
      ret = "File has " + xmlErrors.Rows.Count + " xml errors "; 
      ret += "when validated against our XSD."; 
     } 
    //} else { 
    // ret = "The xml document has incorrect or no namespace.";     
    //} 
    ... 


inne pomysły ...

w równoległej linii myślenia, aby wymienić nazw dostarczonego przez własną rękę, może mógłbyś ustaw doc.DocumentElement.NamespaceURI = "mySpecialNamespace", zastępując w ten sposób nazwę użytkownika elementu głównego.

referencyjny:

add-multiple-namespaces-to-the-root-element

+0

Czy przetestowałeś to, zapewniając im przestrzeń nazw, która nie jest tą, którą dodasz? a także z tym, który dodasz. Dodatkowo, problem polegał na tym, że jeśli dostarczają przestrzeni nazw z przedrostkiem (np. Xmlns: a = "bla"), nie możemy go poprawnie usunąć i dodać własnego. – Martin

+0

@Martin Edytowałem swoją odpowiedź, aby odpowiedzieć na Twój komentarz. –

+0

Problem z usunięciem sprawdzania przestrzeni nazw polega na tym, że weryfikator nie znajdzie niczego, co można sprawdzić. Jeśli dodasz przestrzeń nazw, a węzły mają prefiks przestrzeni nazw, to nie zostaną one zweryfikowane (według mojej wiedzy). Będę musiał pomyśleć o tym, jak iterować elementy i usunąć ich prefiksy ... musi być możliwe ... – Martin

0

Cały sens schematu XSD polega na tym, że tworzy on bez typu XML w silnie typowany XML.

Typ XML można zdefiniować jako połączenie nazwy węzła i przestrzeni nazw.

Jeśli ktoś wyśle ​​Ci XML bez przestrzeni nazw, to pomimo zamiarów XML nie odnosi się do typów zdefiniowanych przez schemat XSD.

Z perspektywy walidacji XML XML jest ważne tak długo, jak

  1. Jest dobrze uformowane
  2. Potwierdza się jakiejkolwiek definicji wpisany XML, określony przez atrybut xmlns
+0

Tak więc najlepsza praktyka mówi, że należy odrzucić XML nie posiadający właściwej (lub żadnej) definicji przestrzeni nazw. Jak sprawdziłbym, czy otrzymany XML jest wtedy mocno wpisany? – Martin

+0

Można sprawdzić kombinację przestrzeni nazw i nazwy węzła głównego, aby sprawdzić, czy wysyłany xml jest prawidłowego typu. –

+0

Czy istnieje elegancki sposób to zrobić w języku C#? – Martin

0

używam XmlSchemaValidationFlags.ReportValidationWarnings flagę. W przeciwnym razie xml z nieznaną przestrzenią nazw (lub bez przestrzeni nazw) będzie w milczeniu przechodzić sprawdzanie poprawności.

public static void Validate(string xml, string schemaPath) 
{ 
    //oops: no ValidationFlag property, cant use linq 
    //var d = XDocument.Parse(xml); 
    //var sc = new XmlSchemaSet(); 
    //sc.Add(null, schemaPath); 
    //sc.CompilationSettings.EnableUpaCheck = false; 
    //d.Validate(sc, null); 

    XmlReaderSettings Xsettings = new XmlReaderSettings(); 
    Xsettings.Schemas.Add(null, schemaPath); 
    Xsettings.ValidationType = ValidationType.Schema; 
    Xsettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings; 
    Xsettings.Schemas.CompilationSettings.EnableUpaCheck = false; 
    Xsettings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack); 

    XmlReader reader = XmlReader.Create(new StringReader(xml), Xsettings); 
    while (reader.Read()) 
    { 
    } 
} 

private static void ValidationCallBack(object sender, ValidationEventArgs e) 
{ 
    if (e.Severity == XmlSeverityType.Warning) 
     throw new Exception(string.Format("No validation occurred. {0}", e.Message)); 
    else 
     throw new Exception(string.Format("Validation error: {0}", e.Message)); 
} 
Powiązane problemy