2012-01-04 20 views
12

Bối cảnh:Bỏ qua không gian tên đã cung cấp khi xác nhận XML với XSD

Chúng tôi đang xây dựng một ứng dụng cho phép khách hàng của chúng tôi để cung cấp dữ liệu trong một định nghĩa trước (. Tức là chúng ta không kiểm soát) định dạng XML. XSD được cung cấp cho chúng tôi bởi một bên thứ ba và chúng tôi hy vọng sẽ nhận được một tệp XML vượt qua xác thực lược đồ trước khi chúng tôi xử lý nó.

Vấn đề:

Các XSD rằng chúng tôi được cung cấp với bao gồm một không gian tên mặc định và mục tiêu, có nghĩa là nếu một khách hàng cung cấp một tập tin XML mà không bao gồm các không gian tên, sau đó xác nhận sẽ vượt qua . Chúng tôi rõ ràng không muốn họ cung cấp những thứ nói rằng họ vượt qua nhưng không nên, nhưng mối quan tâm lớn hơn là xung quanh khối lượng kiểm tra bổ sung mà chúng tôi sẽ cần phải làm trên mỗi yếu tố nếu tôi không thể tìm thấy giải pháp để làm xác thực XML.

Các câu hỏi:

Có thể để buộc .NET để thực hiện xác nhận và bỏ qua những namespace trên XML cung cấp và XSD. tức là bằng cách nào đó "giả định" rằng không gian tên được đính kèm.

  1. Có thể xóa không gian tên trong bộ nhớ, dễ dàng và đáng tin cậy không?
  2. Thực tiễn tốt nhất trong những tình huống này là gì?

Giải pháp mà tôi có cho đến nay:

  1. Tháo namespace từ XSD mọi lúc nó được cập nhật (không nên rất thường xuyên này không nhận được xung quanh một thực tế rằng nếu họ. cung cấp một không gian tên nó sẽ vẫn vượt qua xác nhận
  2. Xóa không gian tên khỏi XSD, VÀ tìm cách tách không gian tên khỏi XML vào mọi lúc.Điều này có vẻ như rất nhiều mã để thực hiện điều gì đó đơn giản. s một số sơ tuyển trước trên tệp XML trước khi nó được xác thực để đảm bảo rằng nó có đúng không gian tên. Dường như sai khi thất bại do không gian tên không hợp lệ nếu nội dung của tệp là chính xác.
  3. Tạo một XSD trùng lặp không có vùng tên, tuy nhiên nếu chúng chỉ cung cấp không gian tên sai hoặc một không gian tên khác, thì nó sẽ vẫn vượt qua.

Ví dụ 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> 

với namespace đó là khác nhau

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

không gian tên nào cả.

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

Vùng tên XML là một điều tốt, tại sao lại chống lại nó? –

+1

đó là điều chúng tôi không thể kiểm soát, tôi muốn đảm bảo rằng khách hàng đang gửi đúng XML, tuy nhiên, nếu khách hàng bỏ lỡ khai báo không gian tên trong XML đã gửi của họ thì tôi muốn nói rằng chúng tôi vẫn có thể xác thực nó. Tôi không muốn chỉ nói "Bạn đã sai lầm, bây giờ sửa nó!" (và vâng tôi sẽ sử dụng các từ tốt hơn, nhưng bạn có ý tưởng). – Martin

Trả lời

6

Đang cố gắng để giải quyết cùng một vấn đề. Tôi nghĩ ra một giải pháp khá sạch sẽ. Để rõ ràng, tôi đã ommited một số xác nhận về các tham số đầu vào.

Đầu tiên, trường hợp: Có một dịch vụ web nhận tệp, được cho là "xml" hợp lệ và hợp lệ đối với XSD. Tất nhiên, chúng tôi không tin tưởng vào "tính đúng đắn" cũng như không hợp lệ với XSD mà "chúng tôi biết" là chính xác.

Mã cho phương pháp webservice như vậy được trình bày bên dưới, tôi nghĩ nó tự giải thích.

Điểm quan tâm chính là thứ tự mà các xác thực đang diễn ra, bạn không kiểm tra không gian tên trước khi tải, bạn kiểm tra sau, nhưng sạch sẽ.

Tôi quyết định tôi có thể sống với một số xử lý ngoại lệ, vì dự kiến ​​hầu hết các tệp sẽ "tốt" và vì đó là cách xử lý khuôn khổ (vì vậy tôi sẽ không đánh với nó).

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 }); 
} 

Bây giờ, xsd sẽ có somthing như:

<?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> 

Và "tốt" XML sẽ là một cái gì đó như:

<?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> 

tôi thử nghiệm "định dạng XML xấu", "đầu vào không hợp lệ theo XSD", "không gian tên không chính xác".

tham khảo:

Read from memorystream

Trying avoid exception handling checking for wellformness

Validating against XSD, catch the errors

Interesting post about inline schema validation


Hi Martin, nhận xét sction là quá ngắn cho câu trả lời của tôi, vì vậy tôi sẽ cung cấp cho nó ở đây, nó có thể hoặc không thể là một câu trả lời hoàn chỉnh, chúng ta hãy cùng nhau cải thiện nó :)

tôi đã thực hiện các bài kiểm tra sau :

  • Test: xmlns = "blaa"
  • Kết quả: các tập tin bị từ chối, vì không gian tên sai.
  • Kiểm tra: xmlns = "http://mydomain.com/ns/upload.xsd" và xmlns: a = "blaa" và các thành phần có "a: someElement"
  • Kết quả: Lỗi trả về tệp cho biết không mong đợi "a: someElement"
  • Kiểm tra: xmlns = "http://mydomain.com/ns/upload.xsd" và xmlns: a = "blaa" và các phần tử có "someElement" với một số thuộc tính bắt buộc bị thiếu
  • Kết quả: các lỗi trở về tập tin nói rằng thuộc tính là mất tích

các chiến lược sau (mà tôi thích) là, nếu d ocument không tuân thủ, sau đó không chấp nhận, nhưng cung cấp một số thông tin về lý do (ví dụ: "không gian tên sai").

Chiến lược này dường như trái với những gì bạn nói trước đây:

Tuy nhiên, nếu một khách hàng bỏ sót việc kê khai không gian tên trong XML trình của họ sau đó tôi muốn nói rằng chúng ta vẫn có thể xác nhận nó. Tôi không muốn chỉ nói "Bạn đã sai lầm, bây giờ sửa nó!"

Trong trường hợp này, có vẻ như bạn chỉ có thể bỏ qua không gian tên được xác định trong XML. Để làm điều đó bạn sẽ bỏ qua xác nhận của đúng namespace:

... 
    // 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.";     
    //} 
    ... 


ý tưởng khác ...

Trong một đường song song tư tưởng, để thay thế cho tên miền không gian được cung cấp bằng cách riêng của bạn, có lẽ bạn có thể thiết lập doc.DocumentElement.NamespaceURI = "mySpecialNamespace" do đó thay thế các namepsace của phần tử gốc.

Reference:

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

+0

Bạn đã thử nghiệm điều này với họ cung cấp một không gian tên mà không phải là một trong những bạn thêm? và cũng với cái bạn thêm vào. Ngoài ra, vấn đề chúng tôi gặp phải là nếu họ cung cấp không gian tên có tiền tố (ví dụ: xmlns: a = "blah"), chúng tôi không thể xóa nó đúng cách và thêm tiền tố của riêng chúng tôi. – Martin

+0

@Martin Tôi đã chỉnh sửa câu trả lời của mình để trả lời nhận xét của bạn. –

+0

Vấn đề với việc loại bỏ kiểm tra không gian tên là trình xác thực sau đó sẽ không tìm thấy bất kỳ điều gì để xác thực. Nếu bạn thêm một không gian tên và các nút có tiền tố không gian tên, thì chúng sẽ không được xác thực (theo kiến ​​thức của tôi). Tôi sẽ phải suy nghĩ về cách lặp lại các phần tử và loại bỏ tiền tố của chúng ... phải có thể ... – Martin

0

Toàn bộ điểm phía sau lược đồ XSD là làm cho XML không được phân loại thành XML được nhập mạnh mẽ.

Loại XML có thể được định nghĩa là kết hợp của tên nút và không gian tên.

Nếu ai đó gửi cho bạn XML không có không gian tên thì bất chấp ý định XML không tham chiếu đến các loại như được định nghĩa bởi lược đồ XSD.

Từ góc độ xác nhận XML XML có giá trị càng lâu càng

  1. Người ta cũng được hình thành
  2. Nó khẳng định với bất kỳ định nghĩa gõ XML, theo quy định của tính xmlns
+0

Vì vậy, thực hành tốt nhất nói để từ chối XML không có định nghĩa không gian tên đúng (hoặc bất kỳ) nào. Làm cách nào để kiểm tra xem XML đã được nhận có được gõ mạnh không? – Martin

+0

Bạn có thể kiểm tra không gian tên và tổ hợp tên nút gốc để xem liệu xml bạn đang gửi có đúng loại không. –

+0

là có một cách thanh lịch để làm điều đó trong C#? – Martin

0

tôi sử dụng XmlSchemaValidationFlags.ReportValidationWarnings cờ. Nếu không, xml với không gian tên không xác định (hoặc không có không gian tên) sẽ âm thầm vượt qua xác thực.

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)); 
}