Codificar NSString para XML/HTML
-
03-07-2019 - |
Pergunta
Existe uma maneira de codificar HTML uma string (NSString) em Objective-C, algo parecido com Server.HtmlEncode em .NET?
Solução
Não há um método NSString que faça isso. Você terá que escrever sua própria função que faz substituições de string. É suficiente fazer as seguintes substituições:
- '&' => "&"
- '"' =>" ""
- '\'' => "'"
- '>' => ">"
- '<' => "<"
Algo assim deve fazer (não tentei):
[[[[[myStr stringByReplacingOccurrencesOfString: @"&" withString: @"&"]
stringByReplacingOccurrencesOfString: @"\"" withString: @"""]
stringByReplacingOccurrencesOfString: @"'" withString: @"'"]
stringByReplacingOccurrencesOfString: @">" withString: @">"]
stringByReplacingOccurrencesOfString: @"<" withString: @"<"];
Outras dicas
Peguei o trabalho do Mike e transformei-o em uma categoria para NSMutableString e NSString
Crie uma categoria para NSMutableString com:
- (NSMutableString *)xmlSimpleUnescape
{
[self replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@""" withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"’" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"–" withString:@"-" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
return self;
}
- (NSMutableString *)xmlSimpleEscape
{
[self replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"\"" withString:@""" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
return self;
}
Faça uma categoria para NSString com:
- (NSString *)xmlSimpleUnescapeString
{
NSMutableString *unescapeStr = [NSMutableString stringWithString:self];
return [unescapeStr xmlSimpleUnescape];
}
- (NSString *)xmlSimpleEscapeString
{
NSMutableString *escapeStr = [NSMutableString stringWithString:self];
return [escapeStr xmlSimpleEscape];
}
* Uma versão Swift 2.0 *
A versão Objective-C é um pouco mais eficiente, pois realiza operações mutáveis na string.No entanto, esta é uma maneira rápida de escapar simples:
extension String
{
typealias SimpleToFromRepalceList = [(fromSubString:String,toSubString:String)]
// See http://stackoverflow.com/questions/24200888/any-way-to-replace-characters-on-swift-string
//
func simpleReplace( mapList:SimpleToFromRepalceList ) -> String
{
var string = self
for (fromStr, toStr) in mapList {
let separatedList = string.componentsSeparatedByString(fromStr)
if separatedList.count > 1 {
string = separatedList.joinWithSeparator(toStr)
}
}
return string
}
func xmlSimpleUnescape() -> String
{
let mapList : SimpleToFromRepalceList = [
("&", "&"),
(""", "\""),
("'", "'"),
("'", "'"),
("’", "'"),
("–", "-"),
(">", ">"),
("<", "<")]
return self.simpleReplace(mapList)
}
func xmlSimpleEscape() -> String
{
let mapList : SimpleToFromRepalceList = [
("&", "&"),
("\"", """),
("'", "'"),
(">", ">"),
("<", "<")]
return self.simpleReplace(mapList)
}
}
Eu poderia ter usado os recursos de ponte do NSString para escrever algo muito semelhante à versão do NSString, mas decidi fazer isso mais rapidamente.
eu uso Caixa de ferramentas do Google para Mac (Funciona no iPhone). Em particular, veja as adições a NSString em GtmnsString+html.h e GtmnsString+xml.h.
Para codificação de URL:
NSString * encodedString = [originalString
stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
Ver Documentação NSString da Apple para mais informações.
Para codificação HTML:
Verificação de saída CfxmlcreatestringByescingentities, que faz parte da biblioteca XML da fundação principal, mas ainda deve fazer o truque.
A rotina da Samets esqueceu o dígito hexadecimal. Aqui está a rotina que eu criei que funciona:
- (NSString*)convertEntities:(NSString*)string
{
NSString *returnStr = nil;
if( string )
{
returnStr = [ string stringByReplacingOccurrencesOfString:@"&" withString: @"&" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@""" withString:@"\"" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"'" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"9" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"’" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"–" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@">" withString:@">" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"<" withString:@"<" ];
returnStr = [ [ NSString alloc ] initWithString:returnStr ];
}
return returnStr;
}
Aqui está uma implementação mais eficiente dessa lógica XML Escape.
+ (NSString*) xmlSimpleEscape:(NSString*)unescapedStr
{
if (unescapedStr == nil || [unescapedStr length] == 0) {
return unescapedStr;
}
const int len = [unescapedStr length];
int longer = ((int) (len * 0.10));
if (longer < 5) {
longer = 5;
}
longer = len + longer;
NSMutableString *mStr = [NSMutableString stringWithCapacity:longer];
NSRange subrange;
subrange.location = 0;
subrange.length = 0;
for (int i = 0; i < len; i++) {
char c = [unescapedStr characterAtIndex:i];
NSString *replaceWithStr = nil;
if (c == '\"')
{
replaceWithStr = @""";
}
else if (c == '\'')
{
replaceWithStr = @"'";
}
else if (c == '<')
{
replaceWithStr = @"<";
}
else if (c == '>')
{
replaceWithStr = @">";
}
else if (c == '&')
{
replaceWithStr = @"&";
}
if (replaceWithStr == nil) {
// The current character is not an XML escape character, increase subrange length
subrange.length += 1;
} else {
// The current character will be replaced, but append any pending substring first
if (subrange.length > 0) {
NSString *substring = [unescapedStr substringWithRange:subrange];
[mStr appendString:substring];
}
[mStr appendString:replaceWithStr];
subrange.location = i + 1;
subrange.length = 0;
}
}
// Got to end of unescapedStr so append any pending substring, in the
// case of no escape characters this will append the whole string.
if (subrange.length > 0) {
if (subrange.location == 0) {
[mStr appendString:unescapedStr];
} else {
NSString *substring = [unescapedStr substringWithRange:subrange];
[mStr appendString:substring];
}
}
return [NSString stringWithString:mStr];
}
+ (NSString*) formatSimpleNode:(NSString*)tagname value:(NSString*)value
{
NSAssert(tagname != nil, @"tagname is nil");
NSAssert([tagname length] > 0, @"tagname is the empty string");
if (value == nil || [value length] == 0) {
// Certain XML parsers don't like empty nodes like "<foo/>", use "<foo />" instead
return [NSString stringWithFormat:@"<%@ />", tagname];
} else {
NSString *escapedValue = [self xmlSimpleEscape:value];
return [NSString stringWithFormat:@"<%@>%@</%@>", tagname, escapedValue, tagname];
}
}
Se você pode usar o nsxmlNode (no OS X) aqui está o truque:
NSString *string = @"test<me>"
NSXMLNode *textNode = [NSXMLNode textWithStringValue:string];
NSString *escapedString = [textNode.XMLString];
Aqui está minha categoria Swift para codificação/decodificação em HTML:
extension String
{
static let htmlEscapedDictionary = [
"&": "&",
""" : "\"",
"'" : "'",
"9" : "'",
"’" : "'",
"–" : "'",
">" : ">",
"<" : "<"]
var escapedHtmlString : String {
var newString = "\(self)"
for (key, value) in String.htmlEscapedDictionary {
newString.replace(value, withString: key)
}
return newString
}
var unescapedHtmlString : String {
let encodedData = self.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)!
return attributedString.string
}
mutating func replace(originalString:String, withString newString:String)
{
let replacedString = self.stringByReplacingOccurrencesOfString(originalString, withString: newString, options: nil, range: nil)
self = replacedString
}
}
Eu acho que um reverso do HtmlescaPedDictionary também poderia ter sido usado no UNESCAPEDHTMLSTRING
Observação: Como Markbau apontou no comentário abaixo: Como Swift não garante a ordem dos dicionários, certifique -se de substituir &
primeiro.
Não tenho certeza de que funcione em todos os casos, mas pode ser mais fácil cercar seu texto com CDATA:
<xmltag><![CDATA[some <b>long</b> <i>xml</i> text]]></xmltag>
O que é CDATA:O queu003C![CDATA[]]> em XML significa?
Reuni um projeto de exemplo rápido usando as respostas de Mike e Tod aqui.
Simplifica o morto de codificação/não codificante:
NSString *html = @"<p>This \"paragraph\" contains quoted & 'single' quoted stuff.</p>";
NSLog(@"Original String: %@", html);
NSString *escapedHTML = [html xmlSimpleEscapeString];
NSLog(@"Escaped String: %@", escapedHTML);
NSString *unescapedHTML = [escapedHTML xmlSimpleUnescapeString];
NSLog(@"Unescaped String: %@", unescapedHTML);
Swift 4
extension String {
var xmlEscaped: String {
return replacingOccurrences(of: "&", with: "&")
.replacingOccurrences(of: "\"", with: """)
.replacingOccurrences(of: "'", with: "'")
.replacingOccurrences(of: ">", with: ">")
.replacingOccurrences(of: "<", with: "<")
}
}
Esta solução mais fácil é criar uma categoria como abaixo:
Aqui está o arquivo de cabeçalho da categoria:
#import <Foundation/Foundation.h>
@interface NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding;
@end
E aqui está a implementação:
#import "NSString+URLEncoding.h"
@implementation NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
(CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
CFStringConvertNSStringEncodingToEncoding(encoding));
}
@end
E agora podemos simplesmente fazer isso:
NSString *raw = @"hell & brimstone + earthly/delight";
NSString *url = [NSString stringWithFormat:@"http://example.com/example?param=%@",
[raw urlEncodeUsingEncoding:NSUTF8StringEncoding]];
NSLog(url);
Os créditos para esta resposta vão para o site abaixo:-
http://madebymany.com/blog/url-encoding-an-nsstring-on-ios
Consulte abaixo Resposta:
NSString *content = global.strPrivacyPolicy;
content = [[[[[content stringByReplacingOccurrencesOfString: @"&" withString: @"&"]
stringByReplacingOccurrencesOfString:@""" withString:@"\" "]
stringByReplacingOccurrencesOfString: @"'" withString:@"'"]
stringByReplacingOccurrencesOfString: @">" withString: @">"]
stringByReplacingOccurrencesOfString: @"<" withString:@"<"];
[_webViewPrivacy loadHTMLString:content baseURL:nil];
Use a mensagem no exemplo abaixo:
anyStringConverted = [anyString stringByReplacingOccurrencesOfString:@"\n" withString:@"<br>"];
Isso converte o comando 'New Line' no código HTML correspondente. Mas para converter símbolos, você deve escrever o número HTML correspondente. Você pode ver a lista completa de números HTML aqui em
Encontrei a única maneira que usa apenas funções internas (não a análise manual) e abrange todos os casos. Requer Appkit/Uikit, além da fundação. Isso é rápido, mas pode ser facilmente Objective-C:
func encodedForHTML() -> String {
// make a plain attributed string and then use its HTML write functionality
let attrStr = NSAttributedString(string: self)
// by default, the document outputs a whole HTML element
// warning: if default apple implementation changes, this may need to be tweaked
let options: [NSAttributedString.DocumentAttributeKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.excludedElements: [
"html",
"head",
"meta",
"title",
"style",
"p",
"body",
"font",
"span"
]
]
// generate data and turn into string
let data = try! attrStr.data(from: NSRange(location: 0, length: attrStr.length), documentAttributes: options)
let str = String(data: data, encoding: .utf8)!
// remove <?xml line
return str.components(separatedBy: .newlines).dropFirst().first!
}