Форум программистов, компьютерный форум, киберфорум
diadiavova
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

Поддержка дополнительных языков программирования во внедренных скриптах XSLT

Запись от diadiavova размещена 07.06.2021 в 11:00
Показов 5047 Комментарии 0

В предыдущем посте я рассматривал возможность реализации поддержки внедренных скриптов в .Net 5 и .Net Core. В развитие темы я подумал, что не стоит ограничиваться языками C# и VB.Net и решил поэкспериментировать и с другими языками программирования. В первую очередь мой выбор пал на языки WSH, то есть Jscript и VBScript. Эти языки поддерживаются библиотекой msxml (пример можно посмотреть здесь), механизм внедрения такой же как и в случае преобразования с помощью XsltCompiledTransform, только в скриптах поддерживаются языки Jscript и VBScript. Я подумал, почему бы не добавить и их. Скажу сразу, что эксперимент оказался удачным только отчасти. Во-первых, когда я все запустил, начали вываливаться сообщения о том, что не хватает ссылок на библиотеки четвертого фреймворка. Конечно, добавив их я решил проблему, но это было уже не очень красиво, поскольку предполагалось их не использовать. Во-вторых, эти языки в упор не понимают переданных им узлов и наборов узлов, хотя и вполне справляются с работой со строками, числами и т. п. Мало того, если в коде XSLT перед передачей в функцию какой-нибудь узел сначала конвертировать в строку или число, то JScript с обработкой справляется, а вот VBScript и этого не может. Понятно, что функциям передаются CLR-типы, невидимые из COM, но решение этой проблемы – задача довольно трудоемкая и, как мне показалось, оно того не стоит. В конце концов я перенес код в приложение .Net Framework 4.7.2, поскольку все равно его библиотеки используются. Ну и пришлось внести некоторые изменения в код. Компиляция теперь выполняется средствами CodeDom, а не Roslyn, встроенная поддержка скриптов реализуется штатными средствами, а все мои дополнения используют собственное пространство имен.
Реализовав поддержку языков WSH, я не стал останавливаться на достигнутом и подумал о том, почему бы не добавить еще что-нибудь, тем более, что эксперимент был не очень удачным, а его код частично можно использовать и для других целей. В результате я добавил поддержку JavaScript и IronPython. Кроме того, реализовал поддержку загрузки объекта расширения из внешней библиотеки.

Общие принципы

Все языки, с которыми я здесь работал, могут быть использованы в .Net-приложениях при помощи движков этих языков, которые не имеют ничего общего с механизмом расширения XSLT, поэтому для реализации поддержки этих языков нам придется прибегнуть к уже использованному ранее методу, а именно – для работы с каждым из движков бы будем использовать специально сгенерированный для этой цели прокси класс. То есть, общий механизм добавления поддержки нового языка выглядит примерно следующим образом.
  1. У нас будет некоторое пространство имен к которому будут относиться все блоки скриптов. Я использовал пространство из прошлой темы urn:my-xslt-extension-object-generator .
  2. С каждым скриптовым блоком будет ассоциироваться какое-нибудь собственное пространство имен и именно его мы будем использовать для добавления объекта расширения, созданного на базе кода этого блока и его префикс также будет использоваться при вызове функций. Я постарался сделать модель максимально похожей на msxsl, чтобы не путаться. Ну в частности атрибуты типа implements-prefix и тому подобное.
  3. При загрузке XSLT из каждого такого блока генерируется код прокси класса, который потом компилируется, создается экземпляр этого класса и уже он добавляется в XsltArgumentList при трансформации.
Для каждого движка нам придется генерировать собственный прокси класс. Несмотря на то, что у них и будут некоторые общие черты, тем не менее, есть и различия, обусловленные тем, что работа с каждым движком несколько отличается. Далее я вкратце опишу некоторые важные моменты работы с каждым из движков.

MSScriptControl и языки WSH

Для поддержки этих языков нам надо будет добавить в проект ссылку на библиотеку Microsoft Script Control 1.0, которую можно найти на вкладке COM при добавлении ссылки. В свойствах ссылки нужно для свойства «Внедрить типы взаимодействия» установить значение False, поскольку нам нужно будет ссылаться на сборку взаимодействия из сборки, которую мы скомпилируем из кода прокси-класса.
В библиотеке нас интересуют два типа: ScriptControl и Procedure. ScriptControl позволяет добавлять код, получать необходимую информацию о нем и выполнять его. Из Procedure мы можем получить имя и количество аргументов отдельной процедуры, чего вполне достаточно для создания метода.
Прокси класс будет устроен следующим образом:
На уровне класса будет объявлено поле, содержащее собственно экземпляр ScriptControl, оно будет инициализировано в конструкторе. Свойству Language нужно присвоить значение JScript или VBScript. В конструкторе же мы создадим переменную code и передадим ей код нашего скрипта, после чего все это дело нужно будет передать как аргумент методу AddCode скриптконтрола. Методы класса будут иметь те же имена, что и имена процедур. Поскольку скриптконтрол не дает информации об именах параметров мы их сами создадим, я использовал имена типа arg1, arg2 и т. д. Типом параметров и возвращаемого значения будет Object.
Возьмем для примера следующий код
JavaScript
1
2
3
4
5
6
7
8
function Func1(x, y, z)
{
    return "This is Func1 value = " + (x + y + z);
}
function Func2(a, b)
{
    return "a * b = " + (a * b);
}
На его базе нам нужно сгенерировать класс следующего вида
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Public Class ProxyClass
 
    Private scriptControl As MSScriptControl.ScriptControl
 
    Public Sub New()
        MyBase.New
        scriptControl = New MSScriptControl.ScriptControl()
        scriptControl.Language = "JScript"
        Dim code As String
        code = "" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "function Func1(x, y, z)" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "{" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    return ""This is Func1 value = "" + (x + y + z)" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) &
            "}" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "function Func2(a, b)" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "{" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    return ""This is Func2 value""" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "}" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10)
        scriptControl.AddCode(code)
    End Sub
 
    Public Overridable Function Func1(ByVal arg1 As Object, ByVal arg2 As Object, ByVal arg3 As Object) As Object
        Return scriptControl.Run("Func1", arg1, arg2, arg3)
    End Function
 
    Public Overridable Function Func2(ByVal arg1 As Object, ByVal arg2 As Object) As Object
        Return scriptControl.Run("Func2", arg1, arg2)
    End Function
End Class
При компиляции нужно будет добавить ссылку на сборку взаимодействия, ссылка на которую есть в основном проекте, так что найти будет несложно. Если потребуются какие-то дополнительные референсы, то можно этот вопрос предусмотреть в коде XSLT. Также как это было реализовано в msxsl можно добавить элементы assembly и все такое. Я этого не делал, но процесс обработки таких элементов аналогичен тому, как такие элементы обрабатывались в прошлом посте.
Как уже было сказано выше, эти языки не видят узлы и наборы узлов, мало того, VBScript не видит даже значений, полученных при конвертировании узла в строку, а может принимать только объекты, созданные с помощью литералов. Так что польза этого всего небольшая и большого смысла в использовании скриптконтрола в данном случае нет.
В XSLT скрипты этого движка выглядят так
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <my:wshscript implements-prefix="vbs" language="vbs">
        <![CDATA[
        Function TestVBS(input)
        
            TestVBS = "VBS got " & input
        End Function
        ]]>
    </my:wshscript>
 
    <my:wshscript implements-prefix="js" language="js">
        <![CDATA[
        function testJS(input)
        {
            return "JScript got " + input;
        }
        ]]>
    </my:wshscript>
Движок jint и JavaSсript 5.1

Описание движка находится здесь. Там же можно скачать все необходимое, но я использовал пакет NuGet. В бета-версиях есть полезные дополнительные возможности и поддержка фич из более поздних версий языка. Я использовал последнюю стабильную версию. В общем и целом принцип использования этого движка примерно такой же как и в случае скриптконтрола. Так что описывать это подробно, думаю, нет большого смысла. Приведу код прокси класса, который генерируется для уже показанного выше кода JavaScript
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Public Class ProxyClass
 
    Private jintEngine As Jint.Engine
 
    Public Sub New()
        MyBase.New
        jintEngine = New Jint.Engine(New System.Action(Of Jint.Options)(AddressOf Me.ConfigInit))
        Dim code As String
        code = "" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "function Func1(x, y, z)" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "{" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    return ""This is Func1 value = "" + (x + y + z)" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) &
            "}" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "function Func2(a, b)" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "{" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    return ""This is Func2 value""" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "}" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10)
        jintEngine.Execute(code)
        jintEngine.SetValue("EngineLocation", "C:\Users\laet\source\repos\XsltExtension\XsltExtension\bin\Debug\XsltExtension.ex" &
                "e")
    End Sub
 
    Private Sub ConfigInit(ByVal cfg As Jint.Options)
        cfg.AllowClr(System.Reflection.[Assembly].LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll"), System.Reflection.[Assembly].LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.dll"), System.Reflection.[Assembly].LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Xml.dll"), System.Reflection.[Assembly].LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Xml.Linq.dll"), System.Reflection.[Assembly].LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll"), System.Reflection.[Assembly].LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.VisualBasic.dll"))
    End Sub
 
    Public Overridable Function Func1(ByVal x As Object, ByVal y As Object, ByVal z As Object) As Object
        Return jintEngine.Invoke("Func1", x, y, z).ToObject
    End Function
 
    Public Overridable Function Func2(ByVal a As Object, ByVal b As Object) As Object
        Return jintEngine.Invoke("Func2", a, b).ToObject
    End Function
End Class
Здесь при создании экземпляра движка ему еще передается ссылка на метод инициализации конфигурации ConfigInit. Я здесь в конфигурации просто добавил список сборок, которые должны быть доступны в коде скрипта. Если этот список нужно расширить, то опять-таки в блок скрипта надо будет ввести элементы assembly с данными о сборках. А в коде предусмотреть этот момент. Для этого в классе ProxyGenerator ( он был в проекте из прошлого поста, просто сейчас он несколько вырос) есть свойство References, представляющее список, в который можно добавить пути к сборкам, прежде чем выполнять генерацию кода. Еще здесь устанавливается значение для переменной EngineLocation, содержащее путь к текущему приложению. Я его использовал в скрипте, поскольку в том же каталоге находился еще один файл, содержимое которого было вставлено в документ. Но, естественно, это сделано просто как пример, так можно передать в скрипт любую другую информацию.
В XSLT скрипт этого движка выглядит так
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    <my:jintscript implements-prefix="jint">
        <![CDATA[
        function quadsFromString(input)
        {
            input.MoveNext();
            return  input.Current.Value.split(",").map(function(i){ return (+i * +i).toString();}).join(",");
        }
        
        function between(n, start, end)
        {
            return n >= start && n <= end;
        }
        
        function textFromFile()
        {
            var io = importNamespace('System.IO')
            var path = io.Path.GetDirectoryName(EngineLocation)
            var txt = io.File.ReadAllText(io.Path.Combine(path, "TextFile1.txt"));
            return txt;
        }
        ]]>
    </my:jintscript>
IronPython

Добавление поддержки разных реализаций EcmaScript – задача малополезная. Все дело в том, что скрипты от вендора поддерживают язык JScript.Net, это не тот же самый язык, однако он совместим (не уверен, что полностью) с EcmaScript 3. То есть в принципе можно написать код на этом языке и, с высокой вероятностью, компилятор его поймет (но не наоборот: код Jscript.Net обычным интерпретаторам JavaScript непонятен). А вот добавить поддержку Python – это уже серьезное расширение функционала. IronPython, насколько я понимаю – это тот же Python, только имеет ряд дополнительных возможностей, необходимых для взаимодействия CLR-типами и платформой .Net вообще. На данный момент поддерживается версия совместимая с Python 2.7, поддержка третьего питона вроде как уже есть в бета-версии. Документация здесь.
Нужно установить пакет NuGet IronPrthon. Работа с движком этого языка немного отличается от работы с предыдущими рассмотренными движками. В частности, функции не вызываются с помощью специального метода, которому передается имя функции и аргументы, а можно получить объект функции и работать с ним как с функцией (делегатом или методом). Особенность здесь заключается в том, что объект функции является динамическим объектом. Из-за этого, например, объект CodeDom из которого будет генерироваться код, не будет подходить для генерации как на VB.Net, так и на C#, из-за того, что в этих языках работа с динамическими объектами немного отличается. Поскольку изначально мы генерируем код на VB.Net, то это не так и важно, тем не менее упомянуть об этом стоило.
Для примера возьмем код, который я использовал в XSLT
Python
1
2
3
4
5
6
7
8
9
10
11
def factorial(number):
    result = 1
    for i in xrange(2, number + 1):
        result *= i
    return result
        
def intsum(nodes):
    sum = 0
    for n in nodes:
        sum += n.ValueAsInt
    return sum
Из него создается следующий класс
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Public Class ProxyClass
 
    Private funcDictionary As System.Collections.Generic.Dictionary(Of String, Object)
 
    Public Sub New()
        MyBase.New
        funcDictionary = New System.Collections.Generic.Dictionary(Of String, Object)()
        Dim code As String = "" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "def factorial(number):" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    result = 1" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    for i in xrange(2, number + 1):" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "  " &
            "      result *= i" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "    return result" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & Global.Microsoft.VisualBasic.ChrW(9) & Global.Microsoft.VisualBasic.ChrW(9) & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "def intsum(nodes):" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & Global.Microsoft.VisualBasic.ChrW(9) & "sum = 0" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & Global.Microsoft.VisualBasic.ChrW(9) & "for n i" &
            "n nodes:" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & Global.Microsoft.VisualBasic.ChrW(9) & Global.Microsoft.VisualBasic.ChrW(9) & "sum += n.ValueAsInt" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & Global.Microsoft.VisualBasic.ChrW(9) & "return sum" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10)
        Dim engine As Microsoft.Scripting.Hosting.ScriptEngine = IronPython.Hosting.Python.CreateEngine
        Dim scope As Microsoft.Scripting.Hosting.ScriptScope = engine.CreateScope
        engine.Execute(code, scope)
        funcDictionary.Add("factorial", scope.GetVariable("factorial"))
        funcDictionary.Add("intsum", scope.GetVariable("intsum"))
    End Sub
 
    Public Overridable Function factorial(ByVal number As Object) As Object
        Return funcDictionary("factorial")(number)
    End Function
 
    Public Overridable Function intsum(ByVal nodes As Object) As Object
        Return funcDictionary("intsum")(nodes)
    End Function
End Class
Здесь вместо того, чтобы на уровне класса объявить поле, в котором будет храниться движок, я создал поле со словарем функций, который заполняется в конструкторе. А методы класса, просто получают нужную функцию по имени и вызывают ее.
При компиляции в референсы я добавил все библиотеки, которыми прирос мой проект с добавлением пакета IronPython. Честно говоря, не знаю, нужны они все или нет.
Код в XSLT
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <my:ipscript implements-prefix="ip">
        <![CDATA[
def factorial(number):
    result = 1
    for i in xrange(2, number + 1):
        result *= i
    return result
        
def intsum(nodes):
    sum = 0
    for n in nodes:
        sum += n.ValueAsInt
    return sum
        
        ]]>
    </my:ipscript>
Добавление внешнего объекта

Эта тема простая, не требующая ни кодогенерации, ни компиляции. Просто в документе размещаем что-то типа такого
XML
1
2
3
4
    <my:external 
        assembly-path="C:\path\to\HelperLibrary.dll"
        implements-prefix="ext"
        type="HelperLibrary.XsltHelper"/>
При загрузке XSLT нужно найти соответствующую сборку загрузить, создать экземпляр указанного типа, и добавить с пространством имен, соответствующим префиксу. В примере во вложении я это все закомментирую, поскольку здесь нужно указывать полный путь к сборке, а на другом компьютере он будет другим. Но указанная библиотека в решении есть. Так что можно будет раскоментировать, указать правильный путь к ней и запустить, все должно работать.

Результаты

XML
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8" ?>
<input>
    <pow base="5" exp="3"/>
    <dblstr>ma</dblstr>
    <mid start="9" length="5">This is input string</mid>
    <split>string1,string2,string3</split>
    <testVBS>vbsinput</testVBS>
    <testJS>jsinput</testJS>
    <quadsFromString>1,2,3,4,5,6,7,8,9</quadsFromString>
    <csv>
        <v>1</v>
        <v>2</v>
        <v>3</v>
        <v>4</v>
        <v>5</v>
    </csv>
    <factorial>6</factorial>
</input>
XSLT
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl s1 s2 my math str vbs js jint ip ext"
xmlns:s1="urn:included-script:s1"
xmlns:s2="urn:included-script:s2"
xmlns:vbs="urn:included-script:vbs"
xmlns:js="urn:included-script:js"
xmlns:jint="urn:included-script:jint"
xmlns:ip="urn:included-script:ironpython"
xmlns:my="urn:my-xslt-extension-object-generator"
xmlns:math="urn:my-xslt-extension-object-generator:math"
xmlns:str="urn:my-xslt-extension-object-generator:strings"
xmlns:ext="urn:my-xslt-extension-object-generator:ext"
                                >
    <xsl:output method="xml" indent="yes"/>
 
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
 
    <xsl:template match="/">
        <result>
            <pow>
                <xsl:value-of select="s1:pow(*/pow/@base, */pow/@exp)"/>
            </pow>
            <dblstr>
                <xsl:value-of select="s2:dblstr(*/dblstr/text())"/>
            </dblstr>
            <exp>
                <xsl:value-of select="math:Exp(1)"/>
            </exp>
            <mid>
                <xsl:value-of select="str:Mid(*/mid/text(), */mid/@start, */mid/@length)"/>
            </mid>
            <split>
                <xsl:for-each select="str:Split(*/split/text(), ',', -1, 1)">
                    <fragment>
                        <xsl:value-of select="."/>
                    </fragment>
                </xsl:for-each>
            </split>
            <testVBS>
                <xsl:value-of select="vbs:TestVBS('qwerty')"/>
            </testVBS>
            <testJS>
                <xsl:value-of select="js:testJS(string(*/testJS/text()))"/>
            </testJS>
            <quadsFromString>
                <xsl:value-of select="jint:quadsFromString(*/quadsFromString/text())"/>
            </quadsFromString>
            <between>
                <xsl:value-of select="jint:between(5,3,8)"/>
            </between>
            <textFromFile>
                <xsl:value-of select="jint:textFromFile()"/>
            </textFromFile>
            <csv>
                <xsl:value-of select="ext:Csv(*/csv/v)"/>
            </csv>
            <factorial>
                <xsl:value-of select="ip:factorial(number(*/factorial/text()))"/>
            </factorial>
            <intsum>
                <xsl:value-of select="ip:intsum(*/csv/v)"/>
            </intsum>
        </result>
    </xsl:template>
 
    <my:static-object assembly-name="mscorlib" type="System.Math" implements-prefix="math"/>
    <my:static-object assembly-name="Microsoft.VisualBasic" type="Microsoft.VisualBasic.Strings" implements-prefix="str"/>
    <my:external 
        assembly-path="C:\path\to\HelperLibrary.dll"
        implements-prefix="ext"
        type="HelperLibrary.XsltHelper"/>
    <msxsl:script implements-prefix="s1" language="visualbasic">
        <![CDATA[
        Public Function pow(base As Double, exp As Double)
            Return Math.Pow(base, exp)
        End Function
        ]]>
    </msxsl:script>
 
    <msxsl:script implements-prefix="s2" language="csharp">
        <![CDATA[
        public string dblstr(string input)
        {
            return input + input;
        }
        ]]>
    </msxsl:script>
 
    <my:wshscript implements-prefix="vbs" language="vbs">
        <![CDATA[
        Function TestVBS(input)
        
            TestVBS = "VBS got " & input
        End Function
        ]]>
    </my:wshscript>
 
    <my:wshscript implements-prefix="js" language="js">
        <![CDATA[
        function testJS(input)
        {
            return "JScript got " + input;
        }
        ]]>
    </my:wshscript>
 
    <my:jintscript implements-prefix="jint">
        <![CDATA[
        function quadsFromString(input)
        {
            input.MoveNext();
            return  input.Current.Value.split(",").map(function(i){ return (+i * +i).toString();}).join(",");
        }
        
        function between(n, start, end)
        {
            return n >= start && n <= end;
        }
        
        function textFromFile()
        {
            var io = importNamespace('System.IO')
            var path = io.Path.GetDirectoryName(EngineLocation)
            var txt = io.File.ReadAllText(io.Path.Combine(path, "TextFile1.txt"));
            return txt;
        }
        ]]>
    </my:jintscript>
    <my:ipscript implements-prefix="ip">
        <![CDATA[
def factorial(number):
    result = 1
    for i in xrange(2, number + 1):
        result *= i
    return result
        
def intsum(nodes):
    sum = 0
    for n in nodes:
        sum += n.ValueAsInt
    return sum
        
        ]]>
    </my:ipscript>
</xsl:stylesheet>
Результат
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-16"?>
<result>
    <pow>125</pow>
    <dblstr>mama</dblstr>
    <exp>2.718281828459045</exp>
    <mid>input</mid>
    <split>
        <fragment>string1</fragment>
        <fragment>string2</fragment>
        <fragment>string3</fragment>
    </split>
    <testVBS>VBS got qwerty</testVBS>
    <testJS>JScript got jsinput</testJS>
    <quadsFromString>1,4,9,16,25,36,49,64,81</quadsFromString>
    <between>true</between>
    <textFromFile>Текст из обновленного файла</textFromFile>
    <csv>1,2,3,4,5</csv>
    <factorial>720</factorial>
    <intsum>15</intsum>
</result>
Во вложении решение, содержащее два проекта: основной, о котором шла речь в посте и HelperLibrary, написанный специально для добавления из него функционала с помощью элемента external. Проект нужно собрать, в XSLT раскомментировать как сам элемент my:external,
XML
1
2
3
4
    <my:external 
        assembly-path="C:\path\to\HelperLibrary.dll"
        implements-prefix="ext"
        type="HelperLibrary.XsltHelper"/>
так и xsl:value-of, включающий вызов функции
XML
1
<xsl:value-of select="ext:Csv(*/csv/v)"/>
А в атрибуте assembly-path указать полный путь к сборке.

>>
Вложения
Тип файла: zip XsltExtension.zip (15.60 Мб, 719 просмотров)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru