Technical Article

Convert XML to String with formatting

,

Recently I needed to display an XML column on SSRS report. XML has just few tags, but tags were different from row to row. I thought no problem, just include it to data set and add that field into table. But I was terribly wrong. It turned out that SQL server converts xml into a single line sting which is not readable as all, but I wanted present it nicely: something similar to how xml displayed in Management Studio, but without colours.

Internet search did not give me good solution so I decided to write own function which renders xml into text with each tag on separate line and proper indents. Below is the function I wrote. Hope it saves time to someone in similar situation.

Cheers,

Andrei

-- =============================================
-- Author:      Andrei Solntsev
-- Create date: Feb 2013
-- Description:Converts xml value to nvarchar with formatting
-- =============================================
ALTER FUNCTION dbo.RenderXMLToString
(
  @xml xml
)
RETURNS nvarchar(max)
AS
BEGIN
  DECLARE @ret nvarchar(max) = '';  -- return value
  DECLARE @s nvarchar(max);         -- string to render
  DECLARE @ss nvarchar(max);        -- single line of text from @s
  DECLARE @NewLine nvarchar(max) = '
';

  DECLARE @Level int = 0, @LineBegins int = 1, @LineEnds int = 0;

  SET @s = CONVERT(nvarchar(max), @xml, 1);

  IF LEN(@s) > 2
  BEGIN
    SET @s = REPLACE(REPLACE(@s, '<', '|<'),'|</','</');                     -- Use | as new line markers
    SET @s = SUBSTRING(@s, 2, LEN(@s)-1);                                    -- remove fist |
    SET @s = REPLACE(@s, '></', '>|</') + '|';

    WHILE @LineBegins < LEN(@s)
    BEGIN
      SET @LineEnds = CHARINDEX('|', @s, @LineBegins+1);
      IF @LineEnds > 0
      BEGIN
        SET @ss = SUBSTRING(@s, @LineBegins, @LineEnds-@LineBegins);

        IF @ss LIKE '<_%>%<[/]_%>' OR @ss LIKE '<_%[/]>'                     -- one line tag on the same level
          SET @ret = @ret + REPLICATE('  ', @Level) + @ss + @NewLine
        ELSE IF @ss LIKE '<[/]_%>'                                           -- ending tag only
        BEGIN
          SET @Level = @Level - 1;
          SET @ret = @ret + REPLICATE('  ', @Level) + @ss + @NewLine;
        END 
        ELSE IF @ss LIKE '<[^/]%[^/]>'                                       -- starting tag only
        BEGIN
          SET @ret = @ret + REPLICATE('  ', @Level) + @ss + @NewLine;
          SET @Level = @Level + 1;
        END 
        ELSE SET @ret = @ret + @ss + @NewLine;                               -- any text string without tags

        SET @LineBegins = @LineEnds+1;
      END ELSE BREAK;
    END
    SET @ret = LEFT(@ret, LEN(@ret)-2);  -- Remove last CRLF
  END
  RETURN @ret
END
GO

Rate

4.5 (2)

You rated this post out of 5. Change rating

Share

Share

Rate

4.5 (2)

You rated this post out of 5. Change rating