从套接字读取大块xml数据并动态解析

时间:2011-08-16 05:42:56

标签: android sockets stream saxparser inflate

我正在开发一个android客户端,它通过TCP套接字从我的java服务器读取xml数据的连续流。服务器在连续响应之间发送'\ n'字符作为分隔符。下面给出的是一个模型实现..

<response1>
   <datas>
      <data>
           .....
           .....
      </data>
      <data>
           .....
           .....
      </data>
      ........
      ........
   </datas>
</response1>\n    <--- \n acts as delimiter ---/> 
<response2>

   <datas>
      <data>
           .....
           .....
      </data>
      <data>
           .....
           .....
      </data>
      ........
      ........
   </datas>
</response2>\n

我希望结构现在清楚了。此响应从服务器zlib压缩传输。因此,我必须首先对从服务器上读取的内容进行充气,然后使用分隔符和解析将响应分开。 我正在使用SAX来解析我的XML

现在我的主要问题是来自服务器的xml响应可能非常大(可以在3到4 MB的范围内)。所以

  • 根据分隔符分隔响应(\ n)我必须使用a stringBuilder 用于存储从套接字读取的响应块 而在某些手机上StringBuilder无法存储字符串 MegaBytes系列。它提供 OutOfMemory 例外,并且来自 像this这样的线程我知道保持大字符串(即使是在 临时基础)不是一个好主意。

  • 接下来我尝试传递inflatorReadStream(后者又传递数据) 来自套接字输入流)作为SAX解析器的输入流(没有 打扰自己分离xml并依赖SAX的查找能力 基于标签的文档结尾)。这一次得到一个回应 解析成功,但后来找到'\ n'分隔符SAX 抛出 ExpatParserParseException 在文档后面说垃圾 元素

  • 在捕捉到 ExpatParserParseException 后,我试着阅读 再次,但抛出异常后,SAX Parser关闭了流,所以 当我再次尝试读/解时,它正在给 IOException 说 输入流已关闭。

下面给出了我所做的代码片段(为清楚起见,删除了所有不相关的try catch块)。

private Socket clientSocket     =   null;
DataInputStream readStream      =   null;
DataOutputStream writeStream        =   null;
private StringBuilder incompleteResponse    =   null;
private AppContext  context     =   null;


public boolean connectToHost(String ipAddress, int port,AppContext myContext){
        context                     =   myContext;
        website                     =   site;
        InetAddress serverAddr          =   null;

    serverAddr                      =   InetAddress.getByName(website.mIpAddress);

    clientSocket                    =   new Socket(serverAddr, port);

    //If connected create a read and write Stream objects..
    readStream   =  new DataInputStream(new InflaterInputStream(clientSocket.getInputStream()));
    writeStream             =   new DataOutputStream(clientSocket.getOutputStream());

    Thread readThread = new Thread(){
            @Override
            public void run(){                              
            ReadFromSocket();                   
        }
    };
    readThread.start();     
    return true;
}


public void ReadFromSocket(){
   while(true){
       InputSource xmlInputSource = new InputSource(readStream);
       SAXParserFactory spf =   SAXParserFactory.newInstance();
       SAXParser sp =   null;
       XMLReader xr =   null;
       try{
           sp   = spf.newSAXParser();
       xr   = sp.getXMLReader();
       ParseHandler xmlHandler =    new ParseHandler(context.getSiteListArray().indexOf(website), context);
       xr.setContentHandler(xmlHandler);
       xr.parse(xmlInputSource);
   //  postSuccessfullParsingNotification();
       }catch(SAXException e){
           e.printStackTrace();
           postSuccessfullParsingNotification();
       }catch(ParserConfigurationException e){
           e.printStackTrace();
           postSocketDisconnectionBroadcast();
           break;
       }catch (IOException e){
           postSocketDisconnectionBroadcast();
           e.printStackTrace();
           e.toString();
           break;
       }catch (Exception e){
           postSocketDisconnectionBroadcast();
           e.printStackTrace();
           break;
       }
    }
}

现在我的问题是

  1. 有没有办法让SAX Parser在打开后忽略垃圾字符 xml响应,而不是抛出异常并关闭流..
  2. 如果没有,有什么方法可以避免内存不足错误 StringBuilder的。坦率地说,我不是一个积极的答案 这个。任何解决方法?

2 个答案:

答案 0 :(得分:2)

  1. 您可以在阅读器或流中使用包装器传递给检测换行符的过滤器,然后关闭解析器并启动一个继续使用该流的新解析器:您的流不是有效的XML并且您赢了你现在已经实现了解析它。看看http://commons.apache.org/io/api-release/org/apache/commons/io/input/CloseShieldInputStream.html
  2. 没有

答案 1 :(得分:1)

如果您的SAX解析器支持推送模型(您自己将原始数据块推入其中并在解析原始数据时触发事件),那么您可以在SAX会话开始时简单地推送您自己的初始XML标记。这将成为顶级文档标记,然后您可以在收到响应时推送响应,就SAX而言,它们将是二级标记。这样,您可以在同一个SAX会话中推送多个响应,然后在OnTagOpen事件中(或者您正在使用的那个),当您在级别1检测到其标记名称时,您将知道何时开始新的响应。