How to use FOR XML PATH('') in a query without escaping special characters?
Asked Answered
I

4

11

I have this query:

SELECT DISTINCT
            f.CourseEventKey,
            (
                SELECT      f.Title + '; ' AS [text()]
                FROM        @Facilities
                WHERE       CourseEventKey = f.CourseEventKey
                ORDER BY    f.Title
                FOR XML PATH('')
            ) Facilities
FROM        @Facilities f

It produces this result set:

CourseEventKey Facilities
-------------- -----------------------------------
29             Test Facility 1; 
30             Memphis Training Room; 
32             Drury Inn & Suites Creve Coeur;

The data is fine, but the & is actually an encoded &, which is not suitable for my purposes.

How can I modify this query to return the original values of the special characters in my data?

Infeudation answered 17/4, 2014 at 17:52 Comment(0)
M
3

I think you're going to have to manually wrap the Facilities inline query block with REPLACE statements to reverse the automatic escaping.

It sounds like what you're wanting to do is concatenate multiple facilities that could present a given course. Have you considered other options? This question has several possible approaches that don't have an issue with escaping your characters.

Mazonson answered 17/4, 2014 at 18:47 Comment(2)
You are correct, that's exactly what I'm trying to do. I'll take a look, but any additional help (especially code examples) are greatly appreciated.Infeudation
For those just finding this, see @Baodad's below (as well as several others on SO), which provides a safe, built-in method.Cima
G
15

Use ,TYPE).value('.','NVARCHAR(MAX)') and your special characters will not be escaped:

SELECT DISTINCT
            f.CourseEventKey,
            (
                SELECT      f.Title + '; ' AS [text()]
                FROM        @Facilities
                WHERE       CourseEventKey = f.CourseEventKey
                ORDER BY    f.Title
                FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)')
            AS Facilities
FROM        @Facilities f

Credit for this goes to Rob Farley.

UPDATE:
I just heard about this new method. I haven't tested it thoroughly yet, and would appreciate any feedback. We can replace [text()] with [processing-instruction(x)], like this

select 'hello & there >' as [processing-instruction(x)] FOR XML PATH('')

will return

<?x hello & there >?>

We just need to strip off the <? ... ?>

Grieg answered 27/4, 2016 at 19:15 Comment(1)
@Struco your suggestion for commenting out the <? ... ?> doesn't scale: sqlfiddle.com/#!18/4981ac/1/0Grieg
M
3

I think you're going to have to manually wrap the Facilities inline query block with REPLACE statements to reverse the automatic escaping.

It sounds like what you're wanting to do is concatenate multiple facilities that could present a given course. Have you considered other options? This question has several possible approaches that don't have an issue with escaping your characters.

Mazonson answered 17/4, 2014 at 18:47 Comment(2)
You are correct, that's exactly what I'm trying to do. I'll take a look, but any additional help (especially code examples) are greatly appreciated.Infeudation
For those just finding this, see @Baodad's below (as well as several others on SO), which provides a safe, built-in method.Cima
N
1

Adding to the latest solution provided by @Baodad, simply REPLACE the <? ... ?>.

SELECT DISTINCT
        f.CourseEventKey,
        REPLACE(
        REPLACE(
                (
                SELECT f.Title + '; ' AS [processing-instruction(x)]
                FROM Facilities
                WHERE CourseEventKey = f.CourseEventKey
                ORDER BY f.Title
                FOR XML PATH('')
                )
                , '<?x','')
                , '?>','') Facilities
FROM Facilities f`

Your output will be:

| CourseEventKey |                                 Facilities |
|----------------|--------------------------------------------|
|             29 |        Test Facility 1;  Test Facility 1;  |
|             29 |      Test Facility 33;  Test Facility 33;  |
|             30 |                    Memphis Training Room;  |
|             31 |                            Another place;  |
|             32 |  Drury Inn & Suites;  Drury Inn & Suites;  |
|             32 |    Yet Another place;  Yet Another place;  |

This works because your final output is not in XML, otherwise REPLACE would simply not work as the characters would remain encoded.

Nowhere answered 3/10, 2018 at 9:9 Comment(2)
Thanks @tgolisch and RoachLord for your pointers, i was trying to avoid hijacking Baodad solution, wich helped me in my issue.Nowhere
Everybody starts somewhere. You'll get the hang of it.Infeudation
C
0

I had some difficulty with this as well. Here is what I found that worked for me.

Select (
    Select some_field As [text()]
    From some_table
    For Xml Path(''), Type, Root('root')
).value('(/root)[1]', 'NVarchar(Max)')
Colettacolette answered 1/6, 2023 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.