2009-08-24 31 views
7

Tôi có tệp bao gồm các tài liệu XML hợp lệ được ghép nối. Tôi muốn tách riêng từng tài liệu XML một cách hiệu quả.Cách phân tích cú pháp hiệu quả các tài liệu XML được nối từ một tệp

Nội dung của tệp được nối sẽ trông như thế này, do đó tệp nối được không phải là một tài liệu XML hợp lệ.

<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 

Mỗi tài liệu XML riêng lẻ khoảng 1-4 KB, nhưng có khả năng vài trăm trong số đó. Tất cả các tài liệu XML tương ứng với cùng một lược đồ XML.

Bất kỳ đề xuất hoặc công cụ nào? Tôi đang làm việc trong môi trường Java.

Chỉnh sửa: Tôi không chắc liệu khai báo xml có xuất hiện trong tài liệu hay không.

Chỉnh sửa: Giả sử rằng mã hóa cho tất cả tài liệu xml là UTF-8.

+1

Giả sử mã hóa ký tự vẫn giữ nguyên cho mỗi mã? Nếu không, điều này trở nên khó khăn hơn bao giờ hết :-) –

+0

Tất cả các tệp sử dụng cùng một mã hóa như bản thân tài liệu sử dụng. Không quan trọng nếu họ nói họ là UTF-8. Nếu tài liệu concatinated được định dạng là UTF-16, tất cả chúng đều là UTF-16. –

Trả lời

3

Như Eamon nói, nếu bạn biết điều <? Xml > sẽ luôn ở đó, chỉ cần phá vỡ điều đó.

Nếu không, hãy tìm thẻ cấp tài liệu kết thúc. Đó là, quét văn bản đếm bao nhiêu cấp độ sâu bạn đang có. Mỗi khi bạn thấy thẻ bắt đầu bằng "<" nhưng không phải là "< /" và điều đó không kết thúc bằng "/ >", hãy thêm 1 vào số lượng độ sâu. Mỗi khi bạn thấy thẻ bắt đầu "< /", trừ đi 1. Mỗi lần bạn trừ 1, hãy kiểm tra xem bạn có đang ở mức 0 không. Nếu vậy, bạn đã đến phần cuối của một tài liệu XML.

+0

Tại sao không chỉ tìm kiếm? – wds

+0

Và một lần nữa, tại sao không loại bỏ các hướng dẫn xử lý thay vào đó, thêm tất cả mọi thứ khác trong một thẻ lớn hơn? Lệnh xử lý không hữu ích nữa vì tất cả các tệp đều sử dụng cùng một mã hóa như tài liệu lớn. Khi chúng biến mất, bao gồm một siêu thẻ chỉ biến nó thành XML hợp lệ một lần nữa. –

+0

Nó phụ thuộc vào yêu cầu tối thượng là gì. Câu hỏi được đặt ra là, Làm thế nào để chia chúng ?, Vì vậy, đó là những gì tôi đã cố gắng để trả lời.Mà không biết những gì các poster ban đầu đang cố gắng để làm với đầu ra, tôi không biết liệu gói tất cả trong một thẻ lớn là một giải pháp khả thi hay không. Nếu nó là, tuyệt vời, đi cho nó. Có thể có các giải pháp tiềm năng khác theo hướng đó. Giống như nếu tất cả các tệp đều chia sẻ một thẻ cấp cao nhất, có thể bạn có thể kết hợp tất cả chúng dưới một thẻ như vậy, tức là loại bỏ các thẻ bắt đầu trên tất cả trừ thẻ đầu tiên và thẻ kết thúc trên tất cả trừ thẻ cuối cùng. – Jay

3

Vì bạn không chắc khai báo sẽ luôn có mặt, bạn có thể loại bỏ tất cả khai báo (ví dụ: <\?xml version.*\?> có thể tìm thấy), thêm <doc-collection>, thêm </doc-collection>, sao cho chuỗi kết quả sẽ là tài liệu xml hợp lệ . Trong đó, bạn có thể truy xuất các tài liệu riêng biệt bằng cách sử dụng (ví dụ) truy vấn XPath /doc-collection/*. Nếu tệp kết hợp có thể đủ lớn để mức tiêu thụ bộ nhớ trở thành vấn đề, bạn có thể cần sử dụng trình phân tích cú pháp phát trực tuyến như Sax, nhưng nguyên tắc vẫn giữ nguyên.

Trong một kịch bản tương tự mà tôi gặp, tôi chỉ đơn giản là đọc tài liệu nối trực tiếp sử dụng một xml-phân tích cú pháp: Mặc dù các tập tin nối có thể không phải là một giá trị xml tài liệu, nó là một xml hợp lệ đoạn (trừ các khai báo lặp lại) - vì vậy, khi bạn tách các khai báo, nếu trình phân tích cú pháp của bạn hỗ trợ phân đoạn cú pháp, thì bạn cũng có thể đọc trực tiếp kết quả. Tất cả các phần tử cấp cao nhất sẽ là các phần tử gốc của các tài liệu được ghép nối.

Tóm lại, nếu bạn loại bỏ tất cả các khai báo, bạn sẽ có một đoạn xml hợp lệ có thể phân tích cú pháp một cách trực tiếp hoặc bằng cách bao quanh nó với một số thẻ.

4

Không chia nhỏ! Thêm một thẻ lớn xung quanh nó! Sau đó, nó sẽ trở thành một tệp XML một lần nữa:

<BIGTAG> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
<?xml version="1.0" encoding="UTF-8"?> 
<someData>...</someData> 
</BIGTAG> 

Bây giờ, sử dụng/BIGTAG/SomeData sẽ cung cấp cho bạn tất cả các gốc XML.


Nếu hướng dẫn xử lý đang diễn ra, bạn luôn có thể sử dụng RegEx để xóa chúng. Việc xóa tất cả các hướng dẫn xử lý dễ dàng hơn là sử dụng RegEx để tìm tất cả các nút gốc. Nếu mã hóa khác với tất cả các tài liệu thì hãy nhớ điều này: toàn bộ tài liệu phải được mã hóa bởi một số loại mã hóa, do đó tất cả các tài liệu XML mà nó bao gồm sẽ sử dụng cùng một mã hóa, bất kể mỗi tiêu đề đang nói gì. Nếu tệp lớn được mã hóa dưới dạng UTF-16 thì không quan trọng nếu các lệnh xử lý XML nói rằng chính XML đó là UTF-8. Nó sẽ không phải là UTF-8 vì toàn bộ tập tin là UTF-16. Mã hóa trong các hướng dẫn xử lý XML đó là không hợp lệ.

Bằng cách hợp nhất chúng thành một tệp, bạn đã thay đổi mã hóa ...


Bằng RegEx, tôi có nghĩa là cụm từ thông dụng. Bạn chỉ cần xóa tất cả văn bản nằm giữa một số <? và một? > không quá khó với biểu thức chính quy và hơi phức tạp hơn nếu bạn đang thử các kỹ thuật thao tác chuỗi khác.

+1

Hướng dẫn xử lý bắt đầu bằng "xml" hoặc "XML" được dành riêng cho các tiêu chuẩn XML, do đó, sử dụng chúng như các PI "tùy chỉnh" như thế này không thực sự hợp lệ. –

+0

Ít nhất trình phân tích cú pháp XML của Firefox không thích điều này ... –

+0

Tôi nghĩ rằng điều này phần lớn là khác với hướng dẫn xử lý –

0

Tôi không có câu trả lời Java, nhưng dưới đây là cách tôi giải quyết vấn đề này với C#.

Tôi tạo ra một lớp có tên XmlFileStreams để quét các tài liệu nguồn cho việc kê khai tài liệu XML và phá vỡ nó lên một cách logic vào nhiều tài liệu:

class XmlFileStreams { 

    List<int> positions = new List<int>(); 
    byte[] bytes; 

    public XmlFileStreams(string filename) { 
     bytes = File.ReadAllBytes(filename); 
     for (int pos = 0; pos < bytes.Length - 5; ++pos) 
      if (bytes[pos] == '<' && bytes[pos + 1] == '?' && bytes[pos + 2] == 'x' && bytes[pos + 3] == 'm' && bytes[pos + 4] == 'l') 
       positions.Add(pos); 
     positions.Add(bytes.Length); 
    } 

    public IEnumerable<Stream> Streams { 
     get { 
      if (positions.Count > 1) 
       for (int i = 0; i < positions.Count - 1; ++i) 
        yield return new MemoryStream(bytes, positions[i], positions[i + 1] - positions[i]); 
     } 
    } 

} 

XmlFileStreams Cách sử dụng:

foreach (Stream stream in new XmlFileStreams(@"c:\tmp\test.xml").Streams) { 
    using (var xr = XmlReader.Create(stream, new XmlReaderSettings() { XmlResolver = null, ProhibitDtd = false })) { 
     // parse file using xr 
    } 
} 

Có một vài điều cẩn thận.

  1. Nó đọc toàn bộ tệp vào bộ nhớ để xử lý. Điều này có thể là một vấn đề nếu tập tin thực sự lớn.
  2. Nó sử dụng tìm kiếm sức mạnh vũ phu đơn giản để tìm các ranh giới tài liệu XML.
1

Đây là câu trả lời của tôi cho phiên bản C#. mã rất xấu hoạt động: - \

public List<T> ParseMultipleDocumentsByType<T>(string documents) 
    { 
     var cleanParsedDocuments = new List<T>(); 
     var serializer = new XmlSerializer(typeof(T)); 
     var flag = true; 
     while (flag) 
     { 
      if(documents.Contains(typeof(T).Name)) 
      { 
       var startingPoint = documents.IndexOf("<?xml"); 
       var endingString = "</" +typeof(T).Name + ">"; 
       var endingPoing = documents.IndexOf(endingString) + endingString.Length; 
       var document = documents.Substring(startingPoint, endingPoing - startingPoint); 
       var singleDoc = (T)XmlDeserializeFromString(document, typeof(T)); 
       cleanParsedDocuments.Add(singleDoc); 
       documents = documents.Remove(startingPoint, endingPoing - startingPoint); 
      } 
      else 
      { 
       flag = false; 
      } 
     } 


     return cleanParsedDocuments; 
    } 

    public static object XmlDeserializeFromString(string objectData, Type type) 
    { 
     var serializer = new XmlSerializer(type); 
     object result; 

     using (TextReader reader = new StringReader(objectData)) 
     { 
      result = serializer.Deserialize(reader); 
     } 

     return result; 
    } 
Các vấn đề liên quan