【问题标题】:StAX - Setting the version and encoding using XMLStreamWriterStAX - 使用 XMLStreamWriter 设置版本和编码
【发布时间】:2011-02-25 23:33:13
【问题描述】:

我正在使用 StAX 创建 XML 文件,然后使用 XSD 验证文件。

创建 XML 文件时出现错误:

javax.xml.stream.XMLStreamException: Underlying stream encoding 'Cp1252' and input paramter for writeStartDocument() method 'UTF-8' do not match.
        at com.sun.xml.internal.stream.writers.XMLStreamWriterImpl.writeStartDocument(XMLStreamWriterImpl.java:1182)

这里是sn-p的代码:

XMLOutputFactory xof =  XMLOutputFactory.newInstance();

try{

  XMLStreamWriter xtw = xof.createXMLStreamWriter(new FileWriter(fileName));
  xtw.writeStartDocument("UTF-8","1.0");} catch(XMLStreamException e) {
  e.printStackTrace();

} catch(IOException ie) {

  ie.printStackTrace();

}

我在 Unix 上运行此代码。有人知道如何设置版本和编码样式吗?

【问题讨论】:

    标签: java xml encoding stax


    【解决方案1】:

    如果使用与 Oracle JRE/JDK 捆绑的默认 XMLStreamWriter,您应该始终

    • 创建XMLStreamWriter,显式设置字符编码:xmlOutputFactory.createXMLStreamWriter(in, encoding)
    • 启动文档并明确设置编码:xmlStreamWriter.writeStartDocument(encoding, version)。编写器不够聪明,无法记住创建编写器时的编码集。但是,它会检查这些编码是否相同。请参阅下面的代码。

    这样,您的文件编码和 XML 声明始终保持同步。尽管在 XML 声明中指定编码是可选的,但 XML 最佳实践是始终指定它。

    这是来自 Oracle (Sun) 实现 (Sjsxp) 的代码:

    String streamEncoding = null;
    if (fWriter instanceof OutputStreamWriter) {
        streamEncoding = ((OutputStreamWriter) fWriter).getEncoding();
    }
    else if (fWriter instanceof UTF8OutputStreamWriter) {
        streamEncoding = ((UTF8OutputStreamWriter) fWriter).getEncoding();
    }
    else if (fWriter instanceof XMLWriter) {
        streamEncoding = ((OutputStreamWriter) ((XMLWriter)fWriter).getWriter()).getEncoding();
    }
    
    if (streamEncoding != null && !streamEncoding.equalsIgnoreCase(encoding)) {
        // If the equality check failed, check for charset encoding aliases
        boolean foundAlias = false;
        Set aliases = Charset.forName(encoding).aliases();
        for (Iterator it = aliases.iterator(); !foundAlias && it.hasNext(); ) {
            if (streamEncoding.equalsIgnoreCase((String) it.next())) {
                foundAlias = true;
            }
        }
        // If no alias matches the encoding name, then report error
        if (!foundAlias) {
            throw new XMLStreamException("Underlying stream encoding '"
                    + streamEncoding
                    + "' and input paramter for writeStartDocument() method '"
                    + encoding + "' do not match.");
        }
    }
    

    【讨论】:

      【解决方案2】:

      从代码中很难确定,但如果您依赖 JDK 1.6 提供的默认 Stax 实现 (Sun sjsxp),我建议升级以使用 Woodstox。 众所周知,它比 Sjsxp 的 bug 更少,支持整个 Stax2 API,并且一直在积极开发和支持(而 Sun 版本刚刚编写,并且修复的 bug 数量有限)。

      但是你代码中的错误是这样的:

      XMLStreamWriter xtw = xof.createXMLStreamWriter(new FileWriter(fileName));
      

      您依赖于默认平台编码(必须是 CP-1252,windows?)。您应该始终明确指定您正在使用的编码。 Stream writer 只是验证您没有做危险的事情,并发现可能导致文档损坏的不一致。非常聪明,这实际上表明这不是默认的 Stax 处理器。 :-)

      (另一个答案也指出了一个正确的解决方法,只需传递 OutputStream 和编码以让 XMLStreamWriter 做正确的事情)

      【讨论】:

        【解决方案3】:

        这应该可行:

        // ...
        Writer writer = new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8");
        XMLStreamWriter xtw = xof.createXMLStreamWriter(writer);
        xtw.writeStartDocument("UTF-8", "1.0");
        // ...
        

        【讨论】:

        • 它有效,但您应该在第一行使用 StandardCharsets.UTF_8,在最后一行使用 StandardCharsets.UTF_8.name(),而不是硬编码“UTF-8”。请注意,它至少需要 Java 1.7(或使用 Charset.forName("UTF-8"))。谢谢。
        【解决方案4】:

        我也会尝试使用带有输出参数的createXMLStreamWriter()

        [EDIT] 试过了,它通过改变 createXMLStreamWriter 行来工作:

        XMLStreamWriter xtw = xof.createXMLStreamWriter(new FileOutputStream(fileName), "UTF-8");
        

        [编辑 2] 做了一个更复杂的测试,记录在案:

        String fileName = "Test.xml";
        XMLOutputFactory xof =  XMLOutputFactory.newInstance();
        XMLStreamWriter xtw = null;
        try
        {
          xtw = xof.createXMLStreamWriter(new FileOutputStream(fileName), "UTF-8");
          xtw.writeStartDocument("UTF-8", "1.0");
          xtw.writeStartElement("root");
          xtw.writeComment("This is an attempt to create an XML file with StAX");
        
          xtw.writeStartElement("foo");
          xtw.writeAttribute("order", "1");
            xtw.writeStartElement("meuh");
            xtw.writeAttribute("active", "true");
              xtw.writeCharacters("The cows are flying high this Spring");
            xtw.writeEndElement();
          xtw.writeEndElement();
        
          xtw.writeStartElement("bar");
          xtw.writeAttribute("order", "2");
            xtw.writeStartElement("tcho");
            xtw.writeAttribute("kola", "K");
              xtw.writeCharacters("Content of tcho tag");
            xtw.writeEndElement();
          xtw.writeEndElement();
        
          xtw.writeEndElement();
          xtw.writeEndDocument();
        }
        catch (XMLStreamException e)
        {
          e.printStackTrace();
        }
        catch (IOException ie)
        {
          ie.printStackTrace();
        }
        finally
        {
          if (xtw != null)
          {
            try
            {
              xtw.close();
            }
            catch (XMLStreamException e)
            {
              e.printStackTrace();
            }
          }
        }
        

        【讨论】:

        • @Anurag:我认为你不应该在@和用户名之间加空格:我没有收到你的问题通知。无论如何,出于好奇,我尝试了我的建议并找到了一个可行的解决方案,请参阅我的编辑。
        • @PhiLho:对不起。我收到另一个错误“前缀不能为空”。我的架构不使用任何前缀。有什么办法可以忽略这个错误。因此,我得到了空白文件。
        • @PhiLho:我正在以同样的方式尝试。它仍然给我错误前缀不能为空。
        • @Anurag:我展示的小样本没有任何错误,包装在一个简单的类中。我不使用架构或其他任何内容。
        • @PhiLho:这是通过使用空白前缀解决的:xtw.setPrefix("", "w3.org/2001/XMLSchema-instance");
        猜你喜欢
        • 2012-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多