Сумма XSLT по значению поиска из другого файла

Я хочу получить общее количество конкретного ученика из следующих двух файлов XML:

--- Файл: mark.xml ----

<marks>
  <mark type="HD">10</mark>
  <mark type="D">8</mark>
  <mark type="C">5</mark>
</marks>

--- Файл: studentRecord.xml ---

<students>
  <student id="1234">
    <grade>HD</grade>
  </student>
  <student id="1234">
    <grade>C</grade>
  </student>
  <student id="1111">
    <grade>D</grade>
  </student>
</students>

Как я могу получить общую оценку ученика, имеющего id 1234? должно быть 15.


person Jimmy    schedule 20.05.2011    source источник
comment
Какую среду программирования/технологию вы используете?   -  person Tao    schedule 20.05.2011
comment
Выполняет ли XSLT запросы к нескольким файлам? Я думал, что для этого вам нужен XQuery (или какая-то хост-среда). Целесообразно ли сначала объединить два документа в один?   -  person Donal Fellows    schedule 20.05.2011
comment
Хороший вопрос. +1. См. мой ответ для полного, короткого и простого решения XSLT, которое на самом деле представляет собой однострочное выражение XPath.   -  person Dimitre Novatchev    schedule 20.05.2011
comment
Был ли мой ответ полезен для вас? Собираетесь принять лучший ответ?   -  person Dimitre Novatchev    schedule 26.05.2011


Ответы (3)


Вот короткое и простое XSLT-решение (на самом деле это просто XPath):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
 <xsl:output method="text"/>

 <my:MarkValues>
        <marks>
            <mark type="HD">10</mark>
            <mark type="D">8</mark>
            <mark type="C">5</mark>
        </marks>
 </my:MarkValues>

 <xsl:template match="/*">
     <xsl:value-of select=
     "sum(document('')/*
           /my:MarkValues/*/*
               [@type = current()/*
                          [@id='1234']/grade
             ]
        )"/>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному XML-документу (с именем «studentRecord.xml»):

<students>
    <student id="1234">
        <grade>HD</grade>
    </student>
    <student id="1234">
        <grade>C</grade>
    </student>
    <student id="1111">
        <grade>D</grade>
    </student>
</students>

выдается требуемый ответ:

15

Если вы хотите сохранить значения меток в отдельном файле (не встроенном в таблицу стилей XSLT, как указано выше), выражение XPath следует немного изменить (только аргумент для document() функция:

sum(document('mark.xml')/*/*
                     [@type = current()/*
                                     [@id='1234']/grade
                     ]
   )

Пояснение:

  1. Использование функции XSLT document().

  2. Использование функции XSLT current().

  3. Использование функции XPath sum().

person Dimitre Novatchev    schedule 20.05.2011
comment
спасибо за этот трюк с XPath. Я блуждал, если это было возможно. Вот почему мой ответ немного отличается (и может быть глупым). Если у вас есть время и удовольствие, я хотел бы узнать ваше мнение (или ответить) об этом другом вопрос. Еще раз спасибо за то, что поделились своими знаниями. - person Emiliano Poggi; 20.05.2011
comment
@empo: Добро пожаловать. Я надеюсь, что вы не ненавидите мои комментарии к вашему решению... :) - person Dimitre Novatchev; 21.05.2011

В следующем преобразовании я сначала создаю переменную с собранными значениями, а затем использую функцию XPath sum() для получения результата. Это не так элегантно и умно, как подход @Dimitre, но я все равно хотел бы опубликовать :)


XSLT 2.0 протестирован в Saxon-HE 9.2.1.1J

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>   

    <xsl:variable name="lookup" select="document('lookup.xml')/marks"/> 

    <xsl:template match="students">

        <xsl:variable name="values">
            <values>
                <xsl:for-each select="student[@id='1234']">
                    <value><xsl:value-of select="$lookup/mark[@type=current()/grade]"/></value>
                </xsl:for-each>
            </values>
        </xsl:variable>

        <xsl:value-of select="sum($values//value)"/>

    </xsl:template>

</xsl:stylesheet>

Результат, примененный к входным данным, указанным в вопросе, представляет собой просто сумму:

 15
person Emiliano Poggi    schedule 20.05.2011
comment
Это не решение XSLT 1.0 — никогда не делайте никаких выводов на основании того факта, что якобы преобразование XSLT 1.0 успешно выполняется процессором XSLT 2.0. Попробуйте это с Saxon 6.5.x, MSXMLx, .NET XslCompiledTransform или любым другим процессором XSLT, совместимым с XSLT 1.0. Тогда исправь свою ошибку, а лучше придумай лучшее решение. Пожалуйста, обратите внимание: я не минусую ваш голос. Я считаю, что наше участие в SO — это опыт обучения, и я был бы рад видеть, как все растут. - person Dimitre Novatchev; 21.05.2011
comment
@Dimitre: я думал, что Saxon смог обработать xsl в соответствии с указанной в нем версией. Теперь я вижу причину предупреждения о версии 1.0. Затем я попробовал свое решение с Saxon 6, но оно не помогло. Если я не ошибаюсь, в XSLT 1.0 я не могу использовать xsl:value-of для таких наборов узлов. Спасибо за ваше замечание. - person Emiliano Poggi; 21.05.2011

Попробуйте этот код С#:

 var studenst = XElement.Load("studentRecord.xml");
 var marks= XElement.Load("marks.xml");

 Dictionary<string, int> marksDic = new Dictionary<string, int>();
 foreach (XElement m in marks.Descendants())
      {
           if (m.Attribute("type") != null)
                marksDic.Add(m.Attribute("type").Value, int.Parse(m.Value));
      }


      foreach (XElement s in studenst.Descendants().Where(x=>(x.Attribute("id") !=null ?int.Parse(x.Attribute("id").Value):0)==id))
      {

           Console.WriteLine(marksDic.Where(x => x.Key == s.Value)
                .Select(x => x.Value).Single());
      }
 }
person mBotros    schedule 20.05.2011