用正确的格式编写XML String?

时间:2012-08-31 01:33:58

标签: xml delphi formatting xml-serialization standards

请原谅我缺乏适当的术语,因为我确信这是一个术语。我正在使用原始字符串编写XML文本(不使用任何类型的XML构建器/解析器,以便于使用)。但是,我面临的问题是,我提供的数据中的某些字符会使标准化失效。例如,&符号。当字符串包含此字符串时,结束解析器将被抛弃。如何正确适应这一点并将字符串转换为XML标准?

我正在将简单字符串写入字符串列表并读取其Text属性,如下所示。请注意子例程A(const S: String);,它是将行添加到XML文件并添加必要缩进的缩短方法。请参阅子例程Standardize,这是我需要填写的内容。

uses Windows, Classes, SysUtils, DB, ADODB, ActiveX;

function TSomething.FetchXML(const SQL: String): String;
var
  L: TStringList;
  Q: TADOQuery;
  X, Y: Integer;
  function Standardize(const S: String): String;
  begin
    Result:= S; //<<<--- Need to convert string to XML standards
  end;
  procedure A(const Text: String; const Indent: Integer = 0);
  var
    I: Integer;
    S: String;
  begin
    if Indent > 0 then
      for I := 0 to Indent do
        S:= S + '  ';
    L.Append(S + Text);
  end;
begin
  Result:= '';
  L:= TStringList.Create;
  try
    Q:= TADOQuery.Create(nil);
    try
      Q.ConnectionString:= FCredentials.ConnectionString;
      Q.SQL.Text:= SQL;
      Q.Open;
      A('<?xml version="1.0" encoding="UTF-8"?>');
      A('<dataset Source="ECatAPI">');
      A('<table>');
      A('<fields>', 1);
      for X := 0 to Q.FieldCount - 1 do begin
        A('<field Name="'+Q.Fields[X].FieldName+'" '+
          'Type="'+IntToStr(Integer(Q.Fields[X].DataType))+'" '+
          'Width="'+IntToStr(Q.Fields[X].DisplayWidth)+'" />', 2);
      end;
      A('</fields>', 1);
      A('<rows>', 1);
      if not Q.IsEmpty then begin
        Q.First;
        while not Q.Eof do begin
          A('<row>', 2);
          for Y:= 0 to Q.FieldCount - 1 do begin
            A('<value Field="'+Q.Fields[Y].FieldName+'">'+
              Standardize(Q.Fields[Y].AsString)+'</value>', 3);
          end;
          A('</row>', 2);
          Q.Next;
        end;
      end;
      A('</rows>', 1);
      A('</table>');
      A('</dataset>');
      Result:= L.Text;
      Q.Close;
    finally
      Q.Free;
    end;
  finally
    L.Free;
  end;
end;

注意

以上是伪代码,复制和修改,无关的事情已被更改/排除......

更多信息

此应用程序是一个独立的Web服务器,提供对数据的只读访问。我只需要编写XML数据,我不需要阅读它。即使我这样做,我也有一个覆盖该部分的XML解析器库。我试图尽可能保持这种轻量级,而不用不必要的物体填充内存。

4 个答案:

答案 0 :(得分:4)

不要手动生成XML。

编写用于转义复杂数据的正确代码(例如XML,HTML或其他XML格式的SGML,转发CDATA)是不值得的。

你逃脱的是just a start。等到有人在您的数据中放入不兼容的内容。

许多数据库支持从查询中创建格式良好的XML(请参阅其他答案),这是您应该研究的方向。

答案 1 :(得分:3)

另一个提示:也许您的数据库支持以XML格式生成结果。

答案 2 :(得分:1)

杰里&#39;解决方案很好。

值得注意的是,现有的VCL程序可以做到这一点。

单元IdStrings具有StrXHtmlEncode()。这与Jerry的解决方案完全相同。

单元HttpApp有HTMLEncode()。这个函数比Jerry的解决方案更有效 - 但是要注意 - 这个过程实际上已经打破了unicode字符串。它在pre unicode编译器中正常工作,但是没有针对unicode正确升级,并且错误从未得到修复。

添加了替换的HttpApp.HTMLEncode()的unicode安全版本如下。它比StringReplace()风格更详细,但在运行时性能方面效率更高。 (它是XML和XHTHML的预定义实体,但不适用于HTML 4)。

function XHTMLEncode( const sRawValue: string): string;
var
  Sp, Rp: PChar;
begin
  SetLength( result, Length( sRawValue) * 10);
  Sp := PChar( sRawValue);
  Rp := PChar( result);
  while Sp^ <> #0 do
  begin
    case Sp^ of
      '&': begin
             FormatBuf( Rp^, 10, '&amp;', 10, []);
             Inc(Rp,4);
           end;
      '<',
      '>': begin
             if Sp^ = '<' then
               FormatBuf(Rp^, 8, '&lt;', 8, [])
             else
               FormatBuf(Rp^, 8, '&gt;', 8, []);
             Inc(Rp,3);
           end;
      '"': begin
             FormatBuf(Rp^, 12, '&quot;', 12, []);
             Inc(Rp,5);
           end;
      '''': begin
             FormatBuf(Rp^, 12, '&apos;', 12, []);
             Inc(Rp,5);
           end;
    else
      Rp^ := Sp^
    end;
    Inc(Rp);
    Inc(Sp);
  end;
  SetLength( result, Rp - PChar( result))
end;

答案 3 :(得分:0)

由于上述问题的评论,我已经实现了一个用适当的名称替换预定义实体的函数。这是新的子程序:

function EncodeXmlStr(const S: String): String;
begin
  Result:= StringReplace(S,      '&',  '&amp;',  [rfReplaceAll]);
  Result:= StringReplace(Result, '''', '&apos;', [rfReplaceAll]);
  Result:= StringReplace(Result, '"',  '&quot;', [rfReplaceAll]);
  Result:= StringReplace(Result, '<',  '&lt;',   [rfReplaceAll]);
  Result:= StringReplace(Result, '>',  '&gt;',   [rfReplaceAll]);
end;