Как получить каждый атрибут в отдельной строке при сериализации XML?

Я пишу данные конфигурации в файл XML. Но XML выглядит очень неструктурированным или как сказать

<Configuration>
  <Ftp Host="LOCALHOST" Port="21"/>
  <Pop3 Host="LOCALHOST" Port="110" Interval="30000"/>
  <Smtp Host="LOCALHOST" Port="25"/>
</Configuration>

Я хотел бы, чтобы это выглядело как

<Configuration>
  <Ftp 
        Host="LOCALHOST" 
        Port="21"
    />
  <Pop3 
        Host="LOCALHOST" 
        Port="110" 
        Interval="30000"
    />
  <Smtp 
        Host="LOCALHOST" 
        Port="25"
    />
</Configuration>

Это возможно любым способом? Вот фрагмент моего кода Delphi для этого. У меня есть функции / процедуры для всех типов, но просто показывает 2 здесь

constructor TConnXml.Create(const FileName: string);
begin
  inherited Create;
  fConfigfile     := FileName;
  fXMLDoc         := TXMLDocument.Create(Application);
  fXMLDoc.Options := [doNodeAutoIndent];
  if FileExists(fConfigfile) then
    fXMLDoc.LoadFromFile(fConfigfile)
  else
    begin
      fXMLDoc.Active := True;
      fXMLDoc.AddChild('Configuration');
      fXMLDoc.SaveToFile(fConfigfile);
    end;
end;

constructor TConnXml.Create;
begin
  Create(SettingsFileBuild);
end;

function TConnXml.ReadString(const Section, Key, Default: string): string;
var
  Node: IXMLNode;
begin
  Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
  if Assigned(Node) and Node.HasAttribute(Key) then
    Result := Node.Attributes[Key]
  else
    Result := Default;
end;

procedure TConnXml.WriteString(const Section, Key, Value: string);
var
  Node: IXMLNode;
begin
  if ReadString(Section, Key, '') = Value then
    Exit;
  Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
  if not Assigned(Node) then
    Node := fXMLDoc.DocumentElement.AddChild(Section);
  Node.Attributes[Key] := Value;
  fModified := True;
  Save;
end;

procedure TConnXml.Save;
begin
  if not fModified then
    Exit;
  if fBackup then
    CopyFile(PChar(fConfigfile), PChar(fConfigfile + '.bak'), False);
  fXMLDoc.Active := True;
  fXMLDoc.SaveToFile(fConfigfile);
  fModified := False;
end;

function TConnXml.ReadBoolean(const Section, Key: string; Default: Boolean): Boolean;
begin
  Result := Boolean(ReadInteger(Section, Key, Integer(Default)));
end;

procedure TConnXml.WriteBoolean(const Section, Key: string; Value: Boolean);
begin
  WriteInteger(Section, Key, Integer(Value));
end;

1 ответ

Решение

Если вы создаете этот XML и он предназначен для настройки, то его цель - сделать его более читабельным. Я часто использую XML для конфигурации и использую атрибуты только тогда, когда они действительно применимы.

Я бы написал это так:

<Configuration>
  <Ftp> 
    <Host>LOCALHOST</Host> 
    <Port>25</Port>
  </Ftp>
  <Pop3> 
    <Host>LOCALHOST/<Host> 
    <Port>110</Port>
    <Interval>30000</Interval>
  </Pop>
  <Smtp> 
    <Host>LOCALHOST</Host> 
    <Port>25</Port>
  </Smtp>
</Configuration>

Использование другого формата, кроме XML, также является решением. Но если вы придерживаетесь XML, то мой ответ - это один из способов организовать XML в удобочитаемой форме. Также, если вы избегаете атрибутов, преобразование в JSON, например, очень просто.

Даже если XML раздут с разметкой, я нахожу его читаемым, если он хорошо структурирован. И хотя это было предназначено для обмена компьютерными данными, я нахожу это очень хорошим для файлов конфигурации. YAML выглядит хорошо, но мне не хватает этой явной структуры:)

Обновить:

В связи с запросом кода я обновил ответ дополнительной информацией. Чтобы получить XML как мой ниже, все, что вам нужно сделать, это изменить одну процедуру. С другой стороны, это базовая обработка XML, поэтому я советую вам изучить ее.

function TConnXml.ReadString(const Section, Key, Default: string): string;
var
  Node: IXMLNode;
  Child: IXMLNode;
begin
  Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);

  if not Assigned(Node) then 
  begin
    Result := Default;
    Exit;
  end;

  Child:= Node.FindNode(Key);

  if not Assigned(Child) then 
  begin
    Result := Default;
    Exit;
  end;

  Result := Child.Text;
end;

procedure TConnXml.WriteString(const Section, Key, Value: string);
var
  Node: IXMLNode;
  Child: IXMLNode;
begin
  if ReadString(Section, Key, '') = Value then
    Exit;

  Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);

  if not Assigned(Node) then
    Node := fXMLDoc.DocumentElement.AddChild(Section);

  Child:= Node.ChildNodes.FindNode(Key);

  if not Assigned(Child) then
    Child:= Node.AddChild(Key);

  Child.Text := Value;
  fModified := True;
  Save;
end;

Я написал это без тестирования, поэтому могут быть некоторые ошибки, но это код, который вы должны использовать.

Другие вопросы по тегам