Tratando para resolver el mismo problema Se me ocurrió lo que creo que es una solución bastante limpia. Para mayor claridad, he omitido alguna validación sobre los parámetros de entrada.
Primero, el escenario: Hay un servicio web que recibe un archivo, que se supone que es "bien formado" xml y válido contra un XSD. Por supuesto, no confiamos en el "bien fomrmness" ni que es válido contra el XSD que "sabemos" es el correcto.
El código para tal método de servicio web se presenta a continuación, creo que se explica por sí mismo.
El principal punto de interés es el orden en el que se realizan las validaciones, no se comprueba el espacio de nombres antes de la carga, se comprueba después, pero limpiamente.
Decidí que podría vivir con un manejo de excepciones, ya que se espera que la mayoría de los archivos sean "buenos" y porque esa es la forma de trabajo marco (así que no voy a luchar contra eso).
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 });
}
Ahora, el xsd tendría algo como:
<?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>
Y la "buena" XML sería algo así como:
<?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>
probé, "malo formato XML", "entrada no válida según XSD", "espacio de nombre incorrecto".
referencias:
Read from memorystream
Trying avoid exception handling checking for wellformness
Validating against XSD, catch the errors
Interesting post about inline schema validation
Hola Martin, el comentario sction es demasiado corto para mi respuesta, así que lo voy a dar aquí, puede o no ser sea una respuesta completa, vamos a mejorar juntos :)
hice las siguientes pruebas :
- prueba: xmlns = "blaa"
- Resultado: el archivo es rechazado, a causa de espacio de nombre equivocado.
- prueba: xmlns = "http://mydomain.com/ns/upload.xsd" y xmlns: a = "blaa" y los elementos habían "a: someElement"
- Resultado: El error diciendo que es retunrs archivos no esperaba "a: someElement"
- Prueba: xmlns = "http://midominio.com/ns/upload.xsd" y xmlns: a = "blaa" y los elementos tenían "algún elemento" con algún atributo requerido que faltaba
- Resultado: el error diciendo presentar declaraciones que el atributo no se encuentra
el estrategia seguida (wich yo prefiero) era, si el d el documento no cumple, entonces no lo acepta, pero brinde información sobre el motivo (ej. "espacio de nombres incorrecto").
Esta estrategia parece contrario a lo que ha dicho anteriormente: sin embargo
, si un cliente pierde la declaración de espacio de nombres en su XML presentado a continuación, me gustaría decir que todavía podemos validarlo. No quiero simplemente decir "¡Te equivocaste, ahora arréglalo!"
En este caso, parece que puede ignorar el espacio de nombre definido en el XML. Para hacer que usted no tome la validación de espacio de nombres correcta:
...
// 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.";
//}
...
Otras ideas ...
En una línea paralela de pensamiento, para reemplazar el espacio de nombres suministrada por su cuenta, tal vez usted podría establecer doc.DocumentElement.NamespaceURI = "mySpecialNamespace"
reemplazando así la pieza del nombre del elemento raíz.
Referencia:
add-multiple-namespaces-to-the-root-element
espacios de nombres XML son una buena cosa, ¿por qué pelear? –
es algo que no podemos controlar, quiero asegurarme de que los clientes envíen el XML correcto; sin embargo, si un cliente no detecta la declaración del espacio de nombres en su XML enviado, me gustaría decir que aún podemos validarlo. No quiero simplemente decir "¡Te equivocaste, ahora arréglalo!" (y sí, usaría mejores palabras, pero entiendes la idea). – Martin