使用 SAX 在 Java 中解析 XML:价值减半

Posted

技术标签:

【中文标题】使用 SAX 在 Java 中解析 XML:价值减半【英文标题】:parsing XML in Java using SAX: value cut in 2 halves 【发布时间】:2013-11-16 09:24:32 【问题描述】:

我正在尝试读取基于 xml 并在 JAVA 中使用 SAX 称为 mzXML 的文件格式。它携带部分编码的质谱数据(具有强度的信号)。

这是感兴趣的条目的样子(周围有更多信息):

    <peaks ... >eJwBgAN//EByACzkZJkHP/NlAceAXLJAckeQ4CIUJz/203q2...</peaks>

可以下载在我的情况下强制错误的完整文件here。

其中一个条目中的字符串包含大约 500 个压缩和 base64 编码的双精度对(信号和强度)。我所做的是解压缩和解码,以获取值(下面的示例中未显示解码)。这在一个小数据集上一切正常。现在我用了一个更大的,我遇到了一个我不明白的问题:

过程characters(ch,start,length) 不会读取前面显示的行中的完整条目。 length-value 似乎很小。

当我刚刚将 peaks 条目打印到控制台时,我没有看到这个问题,因为有很多字母,我没有发现字母丢失了。但是解压失败,当有信息丢失时。当我反复运行这个程序时,它总是在同一点断开同一行而不给出任何异常。如果我通过例如更改 mzXML 文件删除扫描,它会在不同的位置中断。我在 character() 过程中使用断点查看了 currentValue

的内容,发现了这一点

这是概括问题所必需的一段代码:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;

import javax.xml.bind.DatatypeConverter;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class ReadXMLFile 

    public static byte[] decompress(byte[] data) throws IOException, DataFormatException  
        Inflater inflater = new Inflater();  
        inflater.setInput(data); 

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); 
        byte[] buffer = new byte[data.length*2]; 
        while (!inflater.finished())  
            int count = inflater.inflate(buffer); 
            outputStream.write(buffer, 0, count); 
         
        outputStream.close(); 
        byte[] output = outputStream.toByteArray(); 

        return output; 
     

    public static void main(String args[]) 

        try 

            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();

            DefaultHandler handler = new DefaultHandler() 

                boolean peaks = false;

                public void startElement(String uri, String localName,String qName, 
                        Attributes attributes) throws SAXException 

                    if (qName.equalsIgnoreCase("PEAKS")) 
                        peaks = true;
                    
                

                public void endElement(String uri, String localName,
                        String qName) throws SAXException 
                    if (peaks) peaks = false;
                

                public void characters(char ch[], int start, int length) throws SAXException 

                    if (peaks) 
                        String currentValue = new String(ch, start, length);
                        System.out.println(currentValue);
                        try 
                            byte[] array = decompress(DatatypeConverter.parseBase64Binary(currentValue));
                            System.out.println(array[1]);

                         catch (IOException | DataFormatException e) e.printStackTrace();
                        peaks = false;
                    
                
            ;

            saxParser.parse("file1_zlib.mzxml", handler);

         catch (Exception e) e.printStackTrace();
    


有没有更安全的方式来读取大型 xml 文件?你能告诉我错误来自哪里或如何避免它吗?

谢谢,迈克尔

【问题讨论】:

【参考方案1】:

过程characters(ch,start,length) 没有读取前面显示的行中的完整条目。长度值似乎很小。

这正是它的工作方式。来自documentation of ContentHandler

SAX 解析器可以在单个块中返回所有连续的字符数据,也可以将其拆分为多个块。

因此,您不应尝试在 characters 实现中调用 decompress。相反,您应该将获得的字符附加到可扩展缓冲区,并仅在获得相应的endElement 时调用decompress

StringBuilder sb = null;

public void startElement(String uri, String localName,String qName, 
    Attributes attributes) throws SAXException 
    if (qName.equalsIgnoreCase("PEAKS")) 
        sb = new StringBuilder();
    


public void endElement(String uri, String localName, String qName) throws SAXException 
    if (sb == null) return;
    try 
        byte[] array = decompress(DatatypeConverter.parseBase64Binary(sb.toString()));
        System.out.println(array[1]);
     catch (IOException | DataFormatException e) e.printStackTrace();
    sb = null;


public void characters(char ch[], int start, int length) throws SAXException 
    if (sb == null) return;
    String currentValue = new String(ch, start, length);
    sb.appens(currentValue);

【讨论】:

【参考方案2】:

试试这个!使用LinkedList 将标签名称存储在每个startElement() 中,并使用pollLast() 在每个endElement() 中删除最后一个元素。使用String.trim()characters() 获取数据。所以每次characters()函数返回一些实际数据(使用String.length()!=0)你可以将它与LinkedList中的最后一个元素(peekLast())相关联

然后你可以选择append()它或者可以不这样做

【讨论】:

以上是关于使用 SAX 在 Java 中解析 XML:价值减半的主要内容,如果未能解决你的问题,请参考以下文章

在 java 中使用 SAX 解析 XML,不区分大小写。

在 JAVA 中使用 SAX 解析器从 XML 文件中提取文本节点

java使用sax解析xml

Java web——xml文件读取的解析方式(DOM和SAX)

Java XML 解析:使用 SAX 获取内部 XML

使用 SAX 解析常见的 XML 元素