วิธีที่ดีที่สุดในการเข้ารหัสข้อมูลข้อความสำหรับ XML ใน Java?


96

คล้ายกับคำถามนี้มากยกเว้น Java

วิธีที่แนะนำในการเข้ารหัสสตริงสำหรับเอาต์พุต XML ใน Java คืออะไร สตริงอาจมีอักขระเช่น "&", "<" เป็นต้น

คำตอบ:


41

ง่ายมาก: ใช้ไลบรารี XML วิธีนี้จะถูกต้องแทนที่จะต้องมีความรู้โดยละเอียดเกี่ยวกับบิตของข้อกำหนด XML


25
คุณสามารถแนะนำห้องสมุดดังกล่าวได้หรือไม่? (ฉันพบว่ามันน่าแปลกใจที่นี่ไม่ใช่ส่วนมาตรฐานของ Java edition 5 ... เป็นงานทั่วไป)
Tim Cooper

4
XML เป็นส่วนหนึ่งของ Java framework มาตรฐาน - ดูใน org.w3c.sax และ org.w3c.dom อย่างไรก็ตามมีเฟรมเวิร์กที่ใช้งานง่ายกว่าเช่น JDom โปรดทราบว่าอาจไม่มีเมธอด "การเข้ารหัสสตริงสำหรับเอาต์พุต XML" - ฉันขอแนะนำให้ทำงาน XML ทั้งหมดด้วยไลบรารีแทนที่จะทำทีละบิตด้วยการจัดการสตริง
Jon Skeet

1
นี่ไม่ใช่คำแนะนำที่มีประโยชน์เช่นนี้เมื่อส่งออก XHTML - FlyingSaucer ต้องการ XML แต่ไม่มีทางที่ฉันจะสร้างเทมเพลตผ่าน XML lib :) โชคดีที่ StringTemplate ช่วยให้ฉันสามารถหลบหนีวัตถุ String ทั้งหมดได้อย่างรวดเร็ว
Stephen

4
@mice: คำถามถูกแท็ก Java และ Java มีไลบรารี XML จำนวนมาก อันที่จริงมี XML API ที่รวมอยู่ใน Java ดังนั้นจึงไม่จำเป็นต้องเพิ่มอะไรอีก ... แม้ว่าจะไม่ใช่ Java แต่ฉันก็ระมัดระวังในการพัฒนาบนแพลตฟอร์มที่ไม่มี XML API ...
Jon Skeet

2
@mice: DOM API สามารถสร้าง XML ได้อย่างสมบูรณ์แบบ หรือมีไลบรารีของบุคคลที่สามที่ค่อนข้างเล็ก (ไฟล์ jar ของ JDom คือ 114K เป็นต้น) การใช้ XML API ยังคงเป็นวิธีที่แนะนำในการสร้าง XML
Jon Skeet

125

ดังที่คนอื่น ๆ กล่าวถึงการใช้ไลบรารี XML เป็นวิธีที่ง่ายที่สุด หากคุณต้องการหนีตัวเองคุณสามารถดูได้StringEscapeUtilsจากห้องสมุดApache Commons Lang


นี่อาจเป็นหนทางที่จะไปได้หากคุณไม่สนใจเกี่ยวกับความถูกต้องสมบูรณ์เช่นหากคุณกำลังสร้างต้นแบบเข้าด้วยกัน
Chase Seibert

2
ใช้StringEscapeUtils.escapeXml(str)จากcommons-lang. ฉันใช้มันในแอปพลิเคชัน App Engine - ทำงานได้อย่างมีเสน่ห์ นี่คือJava Docสำหรับฟังก์ชันนี้:
Oleg K

เมธอด escapeXml ของ StringEscapeUtils ดูเหมือนจะมีราคาแพงเล็กน้อย มีวิธีการที่มีประสิทธิภาพมากกว่าที่ทำงานบน StringBuffer แทน String หรือไม่?
CKing

วิธีนี้ใช้ได้กับทั้งเนื้อหา XML และแอตทริบิวต์หรือไม่ สำหรับฉันดูเหมือนว่าจะใช้ไม่ได้กับแอตทริบิวต์ มันดูเหมือนจะไม่หลบหนี\t, และ\n \r
Lii

@Lii และ\t, \nหรือ\rความต้องการที่จะหนีออกมา?
Betlista

20

เพียงแค่ใช้.

<![CDATA[ your text here ]]>

วิธีนี้จะอนุญาตให้มีอักขระใด ๆ ยกเว้นตอนจบ

]]>

ดังนั้นคุณสามารถใส่อักขระที่อาจผิดกฎหมายเช่น & และ> ตัวอย่างเช่น.

<element><![CDATA[ characters such as & and > are allowed ]]></element>

อย่างไรก็ตามแอตทริบิวต์จะต้องถูกหลีกเลี่ยงเนื่องจากไม่สามารถใช้บล็อก CDATA ได้


11
ในกรณีส่วนใหญ่นั่นไม่ใช่สิ่งที่คุณควรทำ มีคนใช้แท็ก CDATA ในทางที่ผิดมากเกินไป จุดประสงค์ของ CDATA คือการบอกให้โปรเซสเซอร์ไม่ประมวลผลเป็น XML และส่งผ่านไป หากคุณกำลังพยายามสร้างไฟล์ XML คุณควรสร้าง XML ไม่ใช่แค่ส่งไบต์ผ่านองค์ประกอบการตัดบางส่วน
Mads Hansen

2
@Mads การใช้ CDATA จะทำให้ได้ไฟล์ XML ที่ถูกต้องดังนั้นจึงทำได้ดีพอ ๆ กับการทำแบบ "ถูกวิธี" หากคุณไม่ชอบให้แยกวิเคราะห์หลังจากนั้นเอกลักษณ์จะเปลี่ยนรูปและพิมพ์
Thorbjørn Ravn Andersen

24
หากคุณตัดข้อความในองค์ประกอบ CDATA คุณจะต้องออกจากเครื่องหมายปิด CDATA: "]]>" ... ยกเว้นคุณไม่สามารถหลีกเลี่ยงสิ่งนั้นได้ ดังนั้นคุณต้องแบ่งรหัสของคุณออกเป็นชิ้น ๆ แทนโดยที่คุณใส่ครึ่งหนึ่งของข้อมูลในองค์ประกอบ CDATA หนึ่งและอีกครึ่งหนึ่งในวินาที: <! [CDATA [ข้อมูลนี้มีเครื่องหมายปิด CDATA: "]]]]> <! [CDATA [> "นั่นคือสาเหตุที่ต้องแยกกัน]]> ... ท้ายที่สุดแล้วมันอาจจะง่ายกว่ามากแค่หนี '<', '>' และ '&' แทน แน่นอนว่าแอปจำนวนมากไม่สนใจปัญหาที่อาจเกิดขึ้นกับเครื่องหมายปิด CDATA ในข้อมูล ฉันเดาว่าไม่รู้คือความสุข :)
Stijn de Witt

3
@StijndeWitt ถูกต้องแน่นอน CDATA ไม่ใช่ยาครอบจักรวาลสำหรับการหลบหนีอักขระพิเศษ
dnault

นี่เป็นความคิดที่ไม่ดี CDATA ไม่อนุญาตให้ใช้อักขระใด ๆ ที่อยู่นอกการเข้ารหัสของ XML
Florian F

14

สิ่งนี้ได้ผลดีสำหรับฉันในการจัดเตรียมสตริงข้อความเวอร์ชันที่ใช้ Escape:

public class XMLHelper {

/**
 * Returns the string where all non-ascii and <, &, > are encoded as numeric entities. I.e. "&lt;A &amp; B &gt;"
 * .... (insert result here). The result is safe to include anywhere in a text field in an XML-string. If there was
 * no characters to protect, the original string is returned.
 * 
 * @param originalUnprotectedString
 *            original string which may contain characters either reserved in XML or with different representation
 *            in different encodings (like 8859-1 and UFT-8)
 * @return
 */
public static String protectSpecialCharacters(String originalUnprotectedString) {
    if (originalUnprotectedString == null) {
        return null;
    }
    boolean anyCharactersProtected = false;

    StringBuffer stringBuffer = new StringBuffer();
    for (int i = 0; i < originalUnprotectedString.length(); i++) {
        char ch = originalUnprotectedString.charAt(i);

        boolean controlCharacter = ch < 32;
        boolean unicodeButNotAscii = ch > 126;
        boolean characterWithSpecialMeaningInXML = ch == '<' || ch == '&' || ch == '>';

        if (characterWithSpecialMeaningInXML || unicodeButNotAscii || controlCharacter) {
            stringBuffer.append("&#" + (int) ch + ";");
            anyCharactersProtected = true;
        } else {
            stringBuffer.append(ch);
        }
    }
    if (anyCharactersProtected == false) {
        return originalUnprotectedString;
    }

    return stringBuffer.toString();
}

}

1
stringBuffer.append ("& #" + (int) ch + ";"); ใช้ไม่ได้กับอักขระหลายไบต์ ตอนนี้ฉันกำลังเจอกับตัวอักษรอีโมจิลำดับ UTF8 F0 9F 98 8D
Kylar

14

ลองสิ่งนี้:

String xmlEscapeText(String t) {
   StringBuilder sb = new StringBuilder();
   for(int i = 0; i < t.length(); i++){
      char c = t.charAt(i);
      switch(c){
      case '<': sb.append("&lt;"); break;
      case '>': sb.append("&gt;"); break;
      case '\"': sb.append("&quot;"); break;
      case '&': sb.append("&amp;"); break;
      case '\'': sb.append("&apos;"); break;
      default:
         if(c>0x7e) {
            sb.append("&#"+((int)c)+";");
         }else
            sb.append(c);
      }
   }
   return sb.toString();
}

8
คุณมีข้อบกพร่องอย่างน้อยสองข้อที่ฉันเห็น คนหนึ่งบอบบางอีกคนไม่ได้ ฉันจะไม่มีข้อผิดพลาด - เพราะฉันจะไม่สร้างล้อใหม่ตั้งแต่แรก
Jon Skeet

1
และการวนซ้ำผ่านสตริง Unicode นั้นซับซ้อนกว่าเล็กน้อย ดูที่นี่: stackoverflow.com/q/1527856/402322
สิ้นสุด

1
ไม่แน่ใจว่ามันคือบอบบางt==nullแต่มันควรที่จะพิจารณากรณีที่
Myobis

1
@ user1003916: การหลีกเลี่ยง XML ถูกออกแบบมาเพื่อแปลง & amp; เกิดขึ้นเป็น & amp; นั่นคือวิธีการทำงาน หากคุณตัดตอนสตริงหนีไปแล้วนั่นเป็นความผิดของคุณ
Pointer Null

3
ดีใจกับเวอร์ชั่นสุดท้าย Java SE มีขนาดกะทัดรัดรวดเร็วและมีประสิทธิภาพ การทำในสิ่งที่ต้องทำแทนที่จะดาวน์โหลด bloatware อีก 100 MB จะดีกว่าในหนังสือของฉันเสมอ
Roger

11

คำถามนี้มีอายุแปดขวบแล้วและยังไม่ใช่คำตอบที่ถูกต้อง! ไม่คุณไม่ควรนำเข้า API ของบุคคลที่สามทั้งหมดเพื่อทำงานง่ายๆนี้ คำแนะนำที่ไม่ดี

วิธีการต่อไปนี้จะ:

  • จัดการอักขระนอกระนาบพื้นฐานหลายภาษาอย่างถูกต้อง
  • อักขระหลบหนีที่จำเป็นใน XML
  • หลีกเลี่ยงอักขระที่ไม่ใช่ ASCII ซึ่งเป็นทางเลือก แต่เป็นเรื่องธรรมดา
  • แทนที่อักขระที่ผิดกฎหมายใน XML 1.0 ด้วยอักขระการแทนที่ Unicode ไม่มีตัวเลือกที่ดีที่สุดที่นี่การลบออกก็ใช้ได้เช่นกัน

ฉันพยายามเพิ่มประสิทธิภาพสำหรับกรณีที่พบบ่อยที่สุดในขณะที่ยังคงมั่นใจว่าคุณสามารถไพพ์ / dev / สุ่มผ่านสิ่งนี้และรับสตริงที่ถูกต้องใน XML

public static String encodeXML(CharSequence s) {
    StringBuilder sb = new StringBuilder();
    int len = s.length();
    for (int i=0;i<len;i++) {
        int c = s.charAt(i);
        if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
            c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff);    // UTF16 decode
        }
        if (c < 0x80) {      // ASCII range: test most common case first
            if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
                // Illegal XML character, even encoded. Skip or substitute
                sb.append("&#xfffd;");   // Unicode replacement character
            } else {
                switch(c) {
                  case '&':  sb.append("&amp;"); break;
                  case '>':  sb.append("&gt;"); break;
                  case '<':  sb.append("&lt;"); break;
                  // Uncomment next two if encoding for an XML attribute
//                  case '\''  sb.append("&apos;"); break;
//                  case '\"'  sb.append("&quot;"); break;
                  // Uncomment next three if you prefer, but not required
//                  case '\n'  sb.append("&#10;"); break;
//                  case '\r'  sb.append("&#13;"); break;
//                  case '\t'  sb.append("&#9;"); break;

                  default:   sb.append((char)c);
                }
            }
        } else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
            // Illegal XML character, even encoded. Skip or substitute
            sb.append("&#xfffd;");   // Unicode replacement character
        } else {
            sb.append("&#x");
            sb.append(Integer.toHexString(c));
            sb.append(';');
        }
    }
    return sb.toString();
}

แก้ไข: สำหรับผู้ที่ยังคงยืนยันว่ามันโง่ที่จะเขียนโค้ดของคุณเองสำหรับสิ่งนี้เมื่อมี Java API ที่ดีอย่างสมบูรณ์ในการจัดการกับ XML คุณอาจต้องการทราบว่า StAX API รวมอยู่ใน Oracle Java 8 (ฉันยังไม่ได้ทดสอบคนอื่น ) ล้มเหลวในการเข้ารหัสเนื้อหา CDATA อย่างถูกต้อง: ไม่ได้หลบหนี]]> ลำดับในเนื้อหา ไลบรารีของบุคคลที่สามแม้แต่ไลบรารีที่เป็นส่วนหนึ่งของคอร์ Java ก็ไม่ใช่ตัวเลือกที่ดีที่สุดเสมอไป


+1 สำหรับรหัสแบบสแตนด์อโลน แค่เปรียบเทียบโค้ดของคุณกับการใช้งานแบบฝรั่งฉันสงสัยว่า '\ t', '\ n', '\ r' ล่ะ? ดูบันทึกที่guava docs
jschnasse

2
ไม่จำเป็นต้องหนี \ n, \ r และ \ t แต่ก็ใช้ได้แม้ว่าจะทำให้การจัดรูปแบบดูน่าเกลียดไปหน่อยก็ตาม ฉันได้แก้ไขโค้ดเพื่อแสดงวิธี escsape หากนั่นคือสิ่งที่คุณต้องการ
Mike B

1
นอกจากนี้ไม่มีทางที่จะ "หนี]]>" ใน CDATA
kmkaplan

1
จากนั้นควรปฏิเสธเนื้อหาโดยการโยน IllegalArgumentException ไม่ควรอ้างว่าประสบความสำเร็จ แต่ยังคงแสดงผล XML ที่ไม่ถูกต้องไม่ว่าในกรณีใด
Mike B

แทนการแทนที่ตัวอักษรที่ผิดกฎหมายในรูปแบบ XML 1.0 กับตัวละครเปลี่ยนตัว Unicode คุณสามารถใช้วิธีของฉันที่นี่stackoverflow.com/a/59475093/3882565
stonar96

9

StringEscapeUtils.escapeXml()ไม่หนีอักขระควบคุม (<0x20) XML 1.1 อนุญาตให้ใช้อักขระควบคุม XML 1.0 ไม่ได้ ตัวอย่างเช่น,XStream.toXML()จะทำให้อักขระควบคุมของออบเจ็กต์ Java เป็นอนุกรมอย่างมีความสุขซึ่งตัวแยกวิเคราะห์ XML 1.0 จะปฏิเสธ

หากต้องการหลีกเลี่ยงอักขระควบคุมด้วย Apache commons-lang ให้ใช้

NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))

7
public String escapeXml(String s) {
    return s.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
}

5
การreplaceAllโทรแบบโยงไม่มีประสิทธิภาพมากโดยเฉพาะอย่างยิ่งสำหรับสายอักขระขนาดใหญ่ ทุกการโทรส่งผลให้มีการสร้างอ็อบเจ็กต์ String ใหม่ซึ่งจะค้างอยู่จนกว่าจะรวบรวมขยะ นอกจากนี้การโทรแต่ละครั้งต้องวนซ้ำสตริงอีกครั้ง สิ่งนี้สามารถรวมเป็นลูปแบบแมนนวลเดียวพร้อมการเปรียบเทียบกับถ่านเป้าหมายแต่ละตัวในการวนซ้ำทุกครั้ง
daiscog

นี่ควรเป็นคำตอบที่ได้รับการยอมรับแม้ว่าจะไม่มีประสิทธิภาพก็ตาม มันแก้ปัญหาได้ในบรรทัดเดียว
Stimpson Cat

และก็มีจุดบกพร่องมากมาย ดูความคิดเห็นด้านบนนี้
David Balažic

เพื่อแก้ไขข้อบกพร่องเหล่านี้คุณยังสามารถใช้วิธีการของฉันที่นี่stackoverflow.com/a/59475093/3882565 โปรดทราบว่านี่ไม่ใช่การทดแทน แต่สามารถใช้เพิ่มเติมได้
stonar96

6

ในขณะที่อุดมคติกล่าวว่าให้ใช้ไลบรารี XML แต่ IMHO หากคุณมีแนวคิดพื้นฐานเกี่ยวกับ XML สามัญสำนึกและประสิทธิภาพก็จะบอกว่าเทมเพลตได้ตลอดทาง มันอ่านง่ายกว่าด้วย แม้ว่าการใช้กิจวัตรการหลบหนีของห้องสมุดอาจเป็นความคิดที่ดี

พิจารณาสิ่งนี้: XML คือเขียนขึ้นโดยมนุษย์

ใช้ไลบรารีเพื่อสร้าง XML เมื่อมี XML ของคุณเป็น "วัตถุ" จะจำลองปัญหาของคุณได้ดีขึ้น ตัวอย่างเช่นหากโมดูลที่เสียบได้เข้าร่วมในกระบวนการสร้าง XML นี้

แก้ไข: สำหรับวิธีการหลีกเลี่ยง XML ในเทมเพลตการใช้ CDATA หรือescapeXml(string)จาก JSTL เป็นวิธีแก้ปัญหาที่ดีสองวิธีescapeXml(string)สามารถใช้ได้ดังนี้:

<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<item>${fn:escapeXml(value)}</item>

6

ลักษณะการทำงานของ StringEscapeUtils.escapeXml () ได้เปลี่ยนจาก Commons Lang 2.5 เป็น 3.0 ตอนนี้ไม่หนีอักขระ Unicode ที่มากกว่า 0x7f อีกต่อไป

นี่เป็นสิ่งที่ดีวิธีการเดิมคือการกระตือรือร้นที่จะหลบหนีเอนทิตีที่สามารถแทรกลงในเอกสาร utf8 ได้

Escapers ใหม่ที่จะรวมอยู่ใน Google Guava 11.0 ก็มีแนวโน้มเช่นกัน: http://code.google.com/p/guava-libraries/issues/detail?id=799


1
นี่คือฝรั่งของ Escaper XML: code.google.com/p/guava-libraries/source/browse/guava/src/com/... โดยทั่วไปแล้วฉันพบว่า Guava มีโครงสร้างที่ดีกว่า Apache Commons
jhclark


6

สำหรับผู้ที่มองหาวิธีการเขียนที่รวดเร็วที่สุด: ใช้วิธีการจากapache commons-lang :

อย่าลืมรวมการพึ่งพา:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.5</version> <!--check current version! -->
</dependency>

5

หมายเหตุ: คำถามของคุณเกี่ยวกับการหลบหนีไม่ใช่การเข้ารหัสเข้ารหัสการหลีกเลี่ยงคือการใช้ <ฯลฯ เพื่อให้ตัวแยกวิเคราะห์แยกความแตกต่างระหว่าง "นี่คือคำสั่ง XML" และ "นี่คือข้อความบางส่วน" การเข้ารหัสคือสิ่งที่คุณระบุในส่วนหัว XML (UTF-8, ISO-8859-1 ฯลฯ )

ก่อนอื่นก็เหมือนที่ใคร ๆ พูดกันคือใช้ไลบรารี XML XML ดูเรียบง่าย แต่สิ่งที่เข้ารหัส + หลบหนีนั้นเป็นลัทธิวูดูมืด (ซึ่งคุณจะสังเกตเห็นได้ทันทีที่คุณพบ umlauts และภาษาญี่ปุ่นและสิ่งแปลก ๆ อื่น ๆ เช่น " ตัวเลขเต็มความกว้าง " (& # FF11; คือ 1)) การรักษา XML ที่มนุษย์สามารถอ่านได้เป็นงานของ Sisyphus

ฉันขอแนะนำว่าอย่าพยายามฉลาดเกี่ยวกับการเข้ารหัสข้อความและการหลบหนีใน XML แต่อย่าปล่อยให้สิ่งนั้นหยุดคุณจากความพยายาม แค่จำไว้ว่าเมื่อไหร่ที่มันกัดคุณ (และมันจะ)

ที่กล่าวว่าหากคุณใช้เพียง UTF-8 เพื่อให้อ่านง่ายขึ้นคุณสามารถพิจารณากลยุทธ์นี้:

  • หากข้อความมี '<', '>' หรือ '&' ให้ใส่เข้าไป <![CDATA[ ... ]]>
  • หากข้อความไม่มีอักขระสามตัวนี้อย่าบิดงอ

ฉันใช้สิ่งนี้ในโปรแกรมแก้ไข SQL และช่วยให้นักพัฒนาสามารถตัดและวาง SQL จากเครื่องมือ SQL ของบุคคลที่สามลงใน XML ได้โดยไม่ต้องกังวลเกี่ยวกับการหลบหนี สิ่งนี้ได้ผลเนื่องจาก SQL ไม่สามารถมีเครื่องหมาย umlauts ในกรณีของเราดังนั้นฉันจึงปลอดภัย


5

แม้ว่าฉันจะเห็นด้วยกับ Jon Skeet โดยหลักการแล้วบางครั้งฉันก็ไม่มีตัวเลือกในการใช้ไลบรารี XML ภายนอก และฉันพบว่ามันแปลกที่ทั้งสองฟังก์ชั่นในการ Escape / unescape ค่าธรรมดา (แอตทริบิวต์หรือแท็กไม่ใช่เอกสารฉบับเต็ม) ไม่มีอยู่ในไลบรารี XML มาตรฐานที่มาพร้อมกับ Java

ด้วยเหตุนี้และจากคำตอบต่างๆที่ฉันเห็นโพสต์ไว้ที่นี่และที่อื่น ๆ นี่คือวิธีแก้ปัญหาที่ฉันได้สร้างขึ้น (ไม่มีอะไรทำงานเป็นสำเนา / วางแบบธรรมดา):

  public final static String ESCAPE_CHARS = "<>&\"\'";
  public final static List<String> ESCAPE_STRINGS = Collections.unmodifiableList(Arrays.asList(new String[] {
      "&lt;"
    , "&gt;"
    , "&amp;"
    , "&quot;"
    , "&apos;"
  }));

  private static String UNICODE_NULL = "" + ((char)0x00); //null
  private static String UNICODE_LOW =  "" + ((char)0x20); //space
  private static String UNICODE_HIGH = "" + ((char)0x7f);

  //should only be used for the content of an attribute or tag      
  public static String toEscaped(String content) {
    String result = content;
    
    if ((content != null) && (content.length() > 0)) {
      boolean modified = false;
      StringBuilder stringBuilder = new StringBuilder(content.length());
      for (int i = 0, count = content.length(); i < count; ++i) {
        String character = content.substring(i, i + 1);
        int pos = ESCAPE_CHARS.indexOf(character);
        if (pos > -1) {
          stringBuilder.append(ESCAPE_STRINGS.get(pos));
          modified = true;
        }
        else {
          if (    (character.compareTo(UNICODE_LOW) > -1)
               && (character.compareTo(UNICODE_HIGH) < 1)
             ) {
            stringBuilder.append(character);
          }
          else {
            //Per URL reference below, Unicode null character is always restricted from XML
            //URL: https://en.wikipedia.org/wiki/Valid_characters_in_XML
            if (character.compareTo(UNICODE_NULL) != 0) {
              stringBuilder.append("&#" + ((int)character.charAt(0)) + ";");
            }
            modified = true;
          }
        }
      }
      if (modified) {
        result = stringBuilder.toString();
      }
    }
    
    return result;
  }

ข้างต้นรองรับหลายสิ่งหลายอย่าง:

  1. หลีกเลี่ยงการใช้ตรรกะที่ใช้ถ่านจนกว่าจะต้อง - ปรับปรุงความเข้ากันได้ของ Unicode อย่างแน่นอน
  2. พยายามทำให้มีประสิทธิภาพมากที่สุดเนื่องจากความน่าจะเป็นคือเงื่อนไขที่สอง "if" น่าจะเป็นเส้นทางที่ถูกใช้มากที่สุด
  3. เป็นฟังก์ชันที่บริสุทธิ์ กล่าวคือปลอดภัยต่อเธรด
  4. ปรับให้เหมาะสมอย่างดีกับตัวรวบรวมขยะโดยส่งคืนเนื้อหาของ StringBuilder หากมีการเปลี่ยนแปลงจริงมิฉะนั้นสตริงเดิมจะถูกส่งคืน

ในบางจุดฉันจะเขียนการผกผันของฟังก์ชันนี้เป็น Unescaped () ฉันไม่มีเวลาทำแบบนั้นในวันนี้ เมื่อฉันทำฉันจะมาอัปเดตคำตอบนี้พร้อมรหัส :)


ดูดีสำหรับฉัน ฉันไม่ต้องการเพิ่มโถอื่นในโครงการของฉันด้วยวิธีการเดียวเท่านั้น หากคุณกรุณาอนุญาตฉันขอคัดลอกและวางรหัสของคุณในของฉันได้ไหม
RuntimeException

1
@SatishMotwani แน่นอนคุณสามารถใช้รหัสด้านบนและทำตามที่คุณต้องการ ฉันเข้าใจว่าโค้ดใด ๆ ที่เผยแพร่บน StackOverflow ถือว่าไม่มีลิขสิทธิ์ (ไม่ครอบคลุมเป็นผลงานทั้งหมด) ในทางกลับกันมันคงเป็นเรื่องยากเหลือเกินสำหรับใครบางคนที่จะกดการร้องเรียนการละเมิดลิขสิทธิ์ประเภทใด ๆ และคาดหวังผลลัพธ์ที่จะเกิดขึ้นเอง
messy3quilibrium

1
ขอบคุณที่อนุญาต :-) ฉันจะใช้มัน
RuntimeException

คุณลืมจัดการอักขระ NUL และอาจจะเป็นอย่างอื่นด้วย
David Balažic

@ DavidBalažicเอาล่ะโปรดอธิบายรายละเอียดเพิ่มเติมว่าฉันพลาดอะไรไปบ้าง? โปรดอ่านโค้ดอย่างละเอียดมากขึ้น ฉันจัดการอักขระ Unicode ทุกตัว (จาก 1,111,998) รวมทั้งnullอักขระ คุณสามารถอธิบายคำจำกัดความของค่าสองค่าUNICODE_LOWและUNICODE_HIGH? โปรดอ่านซ้ำifว่าใช้ค่าทั้งสองนี้ ข้อสังเกตnull( \u0000ซึ่งก็คือ(int)0) ไม่อยู่ระหว่างสองค่านี้ อ่านว่ามันกลายเป็น "Escape" อย่างถูกต้องได้อย่างไรเช่นเดียวกับอักขระ Unicode ทั้งหมดที่มีอยู่นอกช่วงUNICODE_LOWและUNICODE_HIGHช่วงโดยใช้&#เทคนิค
busy3quilibrium

3

หากต้องการหลีกเลี่ยงอักขระ XML วิธีที่ง่ายที่สุดคือใช้โครงการ Apache Commons Lang ซึ่งสามารถดาวน์โหลด JAR ได้จาก: http://commons.apache.org/lang/

คลาสคือ: org.apache.commons.lang3.StringEscapeUtils;

มันมีเมธอดชื่อ "escapeXml" ซึ่งจะส่งคืนสตริงที่ใช้ Escape อย่างเหมาะสม


อัปเดต: escapeXml เลิกใช้แล้ว - ใช้ escapeXml10 อ้างอิงcommons.apache.org/proper/commons-lang/javadocs/api-3.3/org/…
Daniel

3

หากคุณกำลังมองหาห้องสมุดเพื่อทำงานให้เสร็จลอง:

  1. เอกสารGuava 26.0 ที่นี่

    return XmlEscapers.xmlContentEscaper().escape(text);

    หมายเหตุ: นอกจากนี้ยังมีไฟล์ xmlAttributeEscaper()

  2. Apache Commons Text 1.4 ได้รับการบันทึกไว้ที่นี่

    StringEscapeUtils.escapeXml11(text)

    หมายเหตุ: นอกจากนี้ยังมีescapeXml10()วิธีการ


1

นี่เป็นวิธีแก้ปัญหาที่ง่ายและเหมาะสำหรับการเข้ารหัสอักขระที่เน้นเสียงด้วย!

String in = "Hi Lârry & Môe!";

StringBuilder out = new StringBuilder();
for(int i = 0; i < in.length(); i++) {
    char c = in.charAt(i);
    if(c < 31 || c > 126 || "<>\"'\\&".indexOf(c) >= 0) {
        out.append("&#" + (int) c + ";");
    } else {
        out.append(c);
    }
}

System.out.printf("%s%n", out);

เอาท์พุต

Hi L&#226;rry &#38; M&#244;e!

ไม่ควรใส่ "31" ในบรรทัดแรกของ "if" be "32" เช่นน้อยกว่าอักขระเว้นวรรค? และถ้า "31" จะต้องยังคงอยู่แล้วไม่ควรได้รับการแก้ไขก็จะอ่าน "ถ้า (c <= 31 || ..." (เท่ากับเพิ่มเติมลงนามดังต่อไปนี้เครื่องหมายน้อยกว่า)
chaotic3quilibrium



0

ใช้JAXPและลืมเกี่ยวกับการจัดการข้อความซึ่งจะดำเนินการให้คุณโดยอัตโนมัติ


ลิงก์ของคุณเป็นภาษาสเปนซึ่งไม่เป็นประโยชน์สำหรับพวกเราส่วนใหญ่ ที่ดีกว่านี้
Vivit

0

พยายามเข้ารหัส XML โดยใช้ Apache XML serializer

//Serialize DOM
OutputFormat format    = new OutputFormat (doc); 
// as a String
StringWriter stringOut = new StringWriter ();    
XMLSerializer serial   = new XMLSerializer (stringOut, 
                                          format);
serial.serialize(doc);
// Display the XML
System.out.println(stringOut.toString());

0

นี่คือสิ่งที่ฉันพบหลังจากค้นหาทุกที่เพื่อหาวิธีแก้ปัญหา:

รับไลบรารี Jsoup:

<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.12.1</version>
</dependency>

จากนั้น:

import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Entities
import org.jsoup.parser.Parser

String xml = '''<?xml version = "1.0"?>
<SOAP-ENV:Envelope
   xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
   SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">

   <SOAP-ENV:Body xmlns:m = "http://www.example.org/quotations">
      <m:GetQuotation>
         <m:QuotationsName> MiscroSoft@G>>gle.com </m:QuotationsName>
      </m:GetQuotation>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''



Document doc = Jsoup.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")), "UTF-8", "", Parser.xmlParser())
doc.outputSettings().charset("UTF-8")
doc.outputSettings().escapeMode(Entities.EscapeMode.base)

println doc.toString()

หวังว่านี่จะช่วยใครบางคนได้


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.