在Java中生成XML时如何转义特殊字符

33

我正在尝试开发一项XML导出功能,以便让我的应用程序用户以XML格式导出他们的数据。我已经准备好并且该功能正常工作,直到它对某些情况开始失败。然后我意识到这是因为需要编码一些特殊字符。例如,数据可能包含&、!、%、'、#等等,这需要得到适当的转义。

我想知道是否有通用的实用程序可用,可以根据XML规范转义所有特殊字符。我在Google上找不到任何信息。

已经有这样的东西了吗?还是有其他方法可以处理这个问题?

这里是我用来生成XML的代码


Document xmldoc = new DocumentImpl();
Element root = xmldoc.createElement("Report");

Element name= xmldoc.createElement((exportData.getChartName() == null) ? "Report" : exportData.getChartName());
if (exportData.getExportDataList().size() > 0
    && exportData.getExportDataList().get(0) instanceof Vector) {
    // First row is the HEADER, i.e name
    Vector name = exportData.getExportDataList().get(0);
    for (int i = 1; i  value = exportData.getExportDataList().get(i);
        Element sub_root = xmldoc.createElement("Data");
        //I had to remove a for loop from here. StackOverflow description field would not take that. :(
            // Insert header row
            Element node = xmldoc.createElementNS(null, replaceUnrecognizedChars(name.get(j)));
            Node node_value = xmldoc.createTextNode(value.get(j));
            node.appendChild(node_value);
            sub_root.appendChild(node);
            chartName.appendChild(sub_root);
        }
    }
}
root.appendChild(name);

// Prepare the DOM document for writing
Source source = new DOMSource(root);

// Prepare the output file
Result result = new StreamResult(file);

// Write the DOM document to the file
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, result);`

示例 XML:


<Data>
    <TimeStamp>2010-08-31 00:00:00.0</TimeStamp>
    <[Name that needs to be encoded]>0.0</[Name that needs to be encoded]>
    <Group_Average>1860.0</Group_Average>
</Data>

我只是想引用一下之前的问题,它似乎涵盖了相同的主题:https://dev59.com/unRC5IYBdhLWcg3wAcQ3 - DGH
可能是Java中将文本数据编码为XML的最佳方法?的重复问题。 - tkruse
3个回答

55
你可以使用apache common lang库来转义字符串。
org.apache.commons.lang.StringEscapeUtils

String escapedXml = StringEscapeUtils.escapeXml("the data might contain & or ! or % or ' or # etc");

但是你需要的是将任何字符串转换为有效的XML标签名称。对于ASCII字符,XML标签名必须以_:a-zA-Z之一开头,后跟_:a-zA-Z0-9.-中的任意数量的字符。

我确信没有库可以为您完成此操作,因此您必须实现自己的函数来将任何字符串转换为匹配此模式的内容,或者将其转换为属性值。

<property name="no more need to be encoded, it should be handled by XML library">0.0</property>

谢谢。这是一个方便的工具,但问题在于它只处理< > " & '。我正在寻找更全面的解决方案。我想要转义的字符串实际上被用作节点名称。我现在还在问题中添加了一个示例XML。 - Salman A. Kagzi
2
根据W3C的XML标准,可以用作元素标记的字符数量有限。您可能希望创建一个通用节点,并将标题作为属性值添加,例如:<data title="现在可以是任何内容"/>。 - gigadot
感谢大家的评论。我认为最好的做法是使用以下格式:<DataPoint Name="[需要编码的名称]" value="0.0" /><DataPoint Name="Group_Average" Value="1860.0"/> 无论如何,我都需要对名称进行编码,为此我可以使用StringEscapeUtils类。 - Salman A. Kagzi
1
escapeXML函数正在转换Unicode字符,但它不应该这样做。 - Mady
但是这也会将 < 和 > 转换。 - Sibish
显示剩余2条评论

1
public class RssParser {
int length;
    URL url;
URLConnection urlConn;
NodeList nodeList;
Document doc;
Node node;
Element firstEle;
NodeList titleList;
Element ele;
NodeList txtEleList;
String retVal, urlStrToParse, rootNodeName;

public RssParser(String urlStrToParse, String rootNodeName){
    this.urlStrToParse = urlStrToParse;
    this.rootNodeName = rootNodeName;

    url=null;
    urlConn=null;
    nodeList=null;
    doc=null;
    node=null;
    firstEle=null;
    titleList=null;
    ele=null;
    txtEleList=null;
    retVal=null;
            doc = null;
    try {
        url = new URL(this.urlStrToParse);
                    // dis is path of url which v'll parse
        urlConn = url.openConnection();

                    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();

        String s = isToString(urlConn.getInputStream());
        s = s.replace("&", "&amp;");
        StringBuilder sb =
                            new StringBuilder
                                    ("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        sb.append("\n"+s);
        System.out.println("STR: \n"+sb.toString());
        s = sb.toString();

        doc = db.parse(urlConn.getInputStream());
        nodeList = doc.getElementsByTagName(this.rootNodeName); 
        //  dis is d first node which
        //  contains other inner element-nodes
        length =nodeList.getLength();
        firstEle=doc.getDocumentElement();
    }
    catch (ParserConfigurationException pce) {
        System.out.println("Could not Parse XML: " + pce.getMessage());
    }
    catch (SAXException se) {
        System.out.println("Could not Parse XML: " + se.getMessage());
    }
    catch (IOException ioe) {
        System.out.println("Invalid XML: " + ioe.getMessage());
    }
    catch(Exception e){
        System.out.println("Error: "+e.toString());
    }
}


public String isToString(InputStream in) throws IOException {
    StringBuffer out = new StringBuffer();
    byte[] b = new byte[512];
    for (int i; (i = in.read(b)) != -1;) {
        out.append(new String(b, 0, i));
    }
    return out.toString();
}

public String getVal(int i, String param){
    node =nodeList.item(i);
    if(node.getNodeType() == Node.ELEMENT_NODE)
    {
        System.out.println("Param: "+param);
        titleList = firstEle.getElementsByTagName(param);
        if(firstEle.hasAttribute("id"))
        System.out.println("hasAttrib----------------");
        else System.out.println("Has NOTNOT      NOT");
        System.out.println("titleList: "+titleList.toString());
    ele = (Element)titleList.item(i);
    System.out.println("ele: "+ele);
        txtEleList = ele.getChildNodes();
    retVal=(((Node)txtEleList.item(0)).getNodeValue()).toString();
    if (retVal == null)
        return null;
            System.out.println("retVal: "+retVal);
    }
return retVal;
}
}

在这段代码中,我创建了一个解析器类,其构造函数接受两个参数;第一个是输入流,用于读取XML文件,第二个是第一个内部节点名称; 然后使用isToStream方法从输入流中检索字符串,该方法返回字符串; 在返回的字符串中,我将一个特殊字符“&”替换为“&”,并在开头添加了XML版本和编码。 - Chintan Raghwani

0
使用以下代码来使用XML转义字符串中的字符。StringEscapeUtils可在apche commons lang3 jar中获得。
StringEscapeUtils.escapeXml11("String to be escaped");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接