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
Przestrzenie nazw XML to dobra rzecz, po co z tym walczyć? –
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