Как я могу исправить уродливый вывод XQuery?

#xml #xsd #xquery

#xml #xsd #xquery

Вопрос:

Вот мой XML-документ:

 <?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="student.xsl"?>
<Students xmlns="www.example.com"     
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="www.example.com student.xsd">
<Student>
    <SSN>622-12-5748</SSN>
    <Name>
        <First-Name>Alexander</First-Name>
        <Last-Name>Mart</Last-Name>
    </Name>
    <Age>26</Age>
    <Institution>UCSF</Institution>
    <Email>Alexander@yahoo.com</Email>
</Student>
</Students>
  

У меня есть этот XQuery:

 xquery version "1.0";

declare namespace xsd = "http://www.w3.org/2001/XMLSchema";
declare namespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
declare default element namespace "http://www.example.com";

for $s in doc("student.xml")/Students/Student
let $firstName := $s/First-Name
let $lastName := $s/Last-Name
let $email := $s/Email
order by $lastName ascending
return 
<row>
    <first-name> { $firstName } </first-name>
    <last-name> { $lastName } </last-name>
    <email> { $email } </email>     
</row>
  

Вывод, который я получаю, это:

 <row xmlns="http://www.example.com">
    <first-name>
        <First-Name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   >Alexander</First-Name>
    </first-name>
    <last-name>
        <Last-Name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  >Mart</Last-Name>
    </last-name>
    <email>
        <Email xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              >Alexander@yahoo.com</Email>
    </email>
</row>
  

Однако все примеры, которые я вижу, выводят что-то вроде этого:

 <row>
    <first-name>Alexander</first-name>
    <last-name>Mart</last-name>
    <email>Alexander@yahoo.com</email>
</row>
  

Как мне красиво отформатировать свой вывод вот так?

Кроме того, почему я получаю это

 <First-Name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  

тег в моих результатах, а не просто <first-name> ? Есть ли способ избавиться от этого? Я добавил declare default element namespace "http://www.example.com"; вверху, потому что я не мог заставить свой XQuery работать без него. Но было бы действительно неплохо, если бы это не отображалось в моих результатах.

Комментарии:

1. Хороший вопрос, 1. Смотрите мой ответ для простого решения и объяснения.

2. Проверьте мой ответ для объяснения вашей проблемы.

Ответ №1:

Использовать:

 declare namespace  x = "http://www.example.com";  

for $s in /x:Students/x:Student
  let $firstName := $s/x:First-Name/text()
    let $lastName := $s/x:Last-Name/text() 
      let $email := $s/x:Email/text() 
  order by $lastName ascending 
     return  
      <row>     
        <first-name> { $firstName } </first-name>     
        <last-name> { $lastName } </last-name>     
        <email> { $email } </email>      
      </row> 
  

когда это применяется к следующему XML-документу (вы не предоставили ни одного XML-документа !!!):

 <Students  xmlns="http://www.example.com">
  <Student>
    <First-Name>A</First-Name>
    <Last-Name>Z</Last-Name>
    <Email>A.Z@T.com</Email>
  </Student>
</Students>
  

получен желаемый результат:

 <row>
   <first-name>A</first-name>
   <last-name>Z</last-name>
   <email>A.Z@T.com</email>
</row>
  

Объяснение: Буквальные элементы результата ( row и т.д.) Находятся в пространстве имен элементов по умолчанию. Решение состоит в том, чтобы не использовать объявление пространства имен элементов по умолчанию.

Комментарии:

1. Ого, это сработало. Большое спасибо! Кроме того, я вернулся назад и добавил свой XML-документ в начало публикации.

2. Итак, я использую XMLSpy для получения результатов. Он по-прежнему выводит это в виде одной строки результатов, например <row><first-name>Goran</first-name><last-name>Alav</last-name><email>Goran@yahoo.com</email> . Я что-то не делаю для создания возврата каретки, чтобы все это отображалось в отдельной строке, или XMLSpy отображает это именно так? Вы знаете?

3. @steph: Сериализация является необязательной функцией XQuery, и для ее реализации не требуется реализаций. Попробуйте указать: declare boundary-space preserve;

4. @Dimitre: Спасибо за это. Мне потребовалось несколько минут, чтобы заставить это работать, потому что я пытался поместить это ниже своих объявлений пространства имен, но как только я переместил его выше тех, которые были проверены файлом. Теперь мой вывод выглядит красиво! Большое спасибо. 🙂

5. @Dimitre: Хорошо, еще один вопрос. Теперь мой последний и первый тег отображаются в одной строке. Итак, между всеми моими результатами я получаю </row><row> одну и ту же строку, хотя все остальное помещается в отдельную строку. Есть ли способ выполнить возврат каретки или что-то эквивалентное между каждым из моих результатов, чтобы теги </row> и <row> отображались в отдельных строках?

Ответ №2:

В XQuery 1.0 нет стандартного способа запрашивать отступ (или другое форматирование) результатов. Многие реализации XQuery имеют свои собственные механизмы. Например, если вы используете Saxon из командной строки, вы можете использовать опцию !отступ = да. Я не знаю, какие опции предоставляет XMLSpy.

Существует опция XQuery 1.0 «declare boundary-space preserve», которая приводит к копированию пробелов в запросе в выходные данные; это может иметь требуемый эффект, хотя, по моему опыту, это также может иметь нежелательные последствия.

Ответ №3:

Во-первых, ваш точный запрос не должен выдавать этот вывод. Вы, должно быть, используете это вместо (из-за объявления пространства имен по умолчанию при вводе):

 declare namespace xsd = "http://www.w3.org/2001/XMLSchema";
declare namespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
declare namespace x = "www.example.com";
declare default element namespace "http://www.example.com";
for $s in /x:Students/x:Student
let $firstName := $s/x:Name/x:First-Name
let $lastName := $s/x:Name/x:Last-Name
let $email := $s/x:Email
order by $lastName ascending
return
<row>
     <first-name> { $firstName } </first-name>
     <last-name> { $lastName } </last-name>
     <email> { $email } </email>
</row>
  

Это действительно вывод:

 <row xmlns="http://www.example.com">
    <first-name>
        <First-Name xmlns="www.example.com"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   >Alexander</First-Name>
    </first-name>
    <last-name>
        <Last-Name xmlns="www.example.com" 
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  >Mart</Last-Name>
    </last-name>
    <email>
        <Email xmlns="www.example.com"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              >Alexander@yahoo.com</Email>
    </email>
</row>
  

ПОЧЕМУ?: Потому что ваш конструктор последовательности { $firstName } выводит узел, на который ссылается $firstName , а не его строковое значение. Это должно быть:

 declare namespace x = "www.example.com";
declare default element namespace "http://www.example.com";
for $s in /x:Students/x:Student
let $firstName := $s/x:Name/x:First-Name
let $lastName := $s/x:Name/x:Last-Name
let $email := $s/x:Email
order by $lastName ascending
return
<row>
     <first-name> { $firstName/string() } </first-name>
     <last-name> { string($lastName) } </last-name>
     <email> { normalize-space($email) } </email>
</row>
  

Вывод:

 <row xmlns="http://www.example.com">
    <first-name>Alexander</first-name>
    <last-name>Mart</last-name>
    <email>Alexander@yahoo.com</email>
</row>