Универсальный просмотр XML в поле HTML-документа



Произвольный XML-документ отображается в ПолеHTMLДокумента через универсальное XSLT-преобразование с возможностью свертывания уровней, аналогично тому, как делает это по умолчанию интернет-браузер, но без использования файловых операций и вызова внешних объектов

Для тех, кто имеет дело с обменами данных и взаимодействием с интернет-приложениями, часто возникает необходимость открыть на просмотр в 1С XML с незнакомой структурой. Что делать?

Разнообразные предшественники

пробовали заниматься автоматическим анализом структуры XML, создавали более или менее универсальные утилиты программного разбора, используя при этом разнообразные «нагруженные» объекты 1С типа ТаблицаЗначений, ДеревоЗначений, либо в худшем (для универсальности и переносимости) случае подключали внешние компоненты.

Но неужели нет простых средств, с минимальными затратами и желательно без программирования?

Конечно, можно поместить текст XML в Поле текстового документа. Но если XML получен из источника, который формировал его автоматически, то скорее всего текст будет неотформатирован и плохо читаем, например:

неформат XML

Заметим, что форматирование XML задача несложная. Ее стандартно решает объект ЗаписьXML (свойство Отступ либо объект ПараметрыЗаписиXML), примерно так:

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ИсходныйТекстXML);

ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку(Новый ПараметрыЗаписиXML(, , Истина, Истина));//здась как раз и содержится явное указание 1С отформатировать XML при записи

ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
ИсходныйТекстXML= ЗаписьXML.Закрыть();

В результате получаем читаемый документ:

формат XML

 Но так отобразить может только в поле текстового документа (причем без подсветки синтаксиса и без свертывания уровней).

Но при работе с интернет задача же часто стоит отображать XML именно в поле HTML-документа. Как?

Если в ПолеHTMLДокумента поместить некий произвольный XML, то 1С ничего не отобразит.

Не поможет и попытка предварительного чтения XML объектами 1С, к примеру:

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(OriginXML);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
ЗаписьHTML = Новый ЗаписьHTML;
ЗаписьHTML.УстановитьСтроку();
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьHTML);
XML2HTML = ЗаписьHTML.Закрыть();

Однако известно, что в отличие от ПолеHTMLДокумента из 1С, интернет-броузеры умеют отображать текст произвольых XML-документов. Примерно так:

формат как в IE

Оказывается, делают они это с помощью встроенных XSL-таблиц. Для IE в Windows такая таблица расположена на локальном компьютере по адресу: res://msxml3.dll/defaultss.xsl   ( см. например http://www.script-coding.com/XSL.html или http://farinadesign.narod.ru/XMLtech/Lectures/DSO.html )

Поэтому задачу полностью решает использование XSL-преобразования (идея взята из //infostart.ru/public/184288/ ):

Преобразование = Новый ПреобразованиеXSL;
Преобразование.ЗагрузитьИзСтроки(ТекстПреобразованияXSL);
XML2HTML = Преобразование.ПреобразоватьИзСтроки(ИсходныйТекстXML);

Дополнительного программирования в 1С не требуется! Требуется только получитьподходящий default.xsl файл. Причем, естественно, варианты таких файлов могут отличаться. Предлагаю использовать следующий, полученный из предыдущих версий IE, исходник xsl-преобразования:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="no" method="html"/>
<xsl:template match="/">
<HTML>
<HEAD>
<SCRIPT>
<xsl:comment><![CDATA[
function f(e){
if (e.className=="ci") {
if (e.children(0).innerText.indexOf("
")>0) fix(e,"cb");
}
if (e.className=="di") {
if (e.children(0).innerText.indexOf("
")>0) fix(e,"db");
} e.id="";
}
function fix(e,cl){
e.className=cl;
e.style.display="block";
j=e.parentElement.children(0);
j.className="c";
k=j.children(0);
k.style.visibility="visible";
k.href="#";
}
function ch(e) {
mark=e.children(0).children(0);
if (mark.innerText=="+") {
mark.innerText="-";
for (var i=1;i<e.children.length;i++) {
e.children(i).style.display="block";
}
}
else if (mark.innerText=="-") {
mark.innerText="+";
for (var i=1;i<e.children.length;i++) {
e.children(i).style.display="none";
}
}
}
function ch2(e) {
mark=e.children(0).children(0);
contents=e.children(1);
if (mark.innerText=="+") {
mark.innerText="-";
if (contents.className=="db"||contents.className=="cb") {
contents.style.display="block";
}
else {
contents.style.display="inline";
}
}
else if (mark.innerText=="-") {
mark.innerText="+";
contents.style.display="none";
}
}
function cl() {
e=window.event.srcElement;
if (e.className!="c") {
e=e.parentElement;
if (e.className!="c") {
return;
}
}
e=e.parentElement;
if (e.className=="e") {
ch(e);
}
if (e.className=="k") {
ch2(e);
}
}
function ex(){}
function h(){window.status=" ";}
document.onclick=cl;
]]></xsl:comment>
</SCRIPT>
<STYLE>
BODY {font:x-small 'Verdana'; margin-right:1.5em}
.c  {cursor:hand}
.b  {color:red; font-family:'Courier New'; font-weight:bold;
text-decoration:none}
.e  {margin-left:1em; text-indent:-1em; margin-right:1em}
.k  {margin-left:1em; text-indent:-1em; margin-right:1em}
.t  {color:#990000}
.xt {color:#990099}
.ns {color:red}
.dt {color:green}
.m  {color:blue}
.tx {font-weight:bold}
.db {text-indent:0px; margin-left:1em; margin-top:0px;
margin-bottom:0px;padding-left:.3em;
border-left:1px solid #CCCCCC; font:small Courier}
.di {font:small Courier}
.d  {color:blue}
.pi {color:blue}
.cb {text-indent:0px; margin-left:1em; margin-top:0px;
margin-bottom:0px;padding-left:.3em; font:small Courier;
color:#888888}
.ci {font:small Courier; color:#888888}
PRE {margin:0px; display:inline}
</STYLE>
</HEAD>
<BODY class="st">
<xsl:apply-templates/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="processing-instruction()">
<DIV class="e">
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">
<xsl:text>&lt;?</xsl:text>
</SPAN>
<SPAN class="pi">
<xsl:value-of select="name(.)"/>
<xsl:value-of select="."/>
</SPAN>
<SPAN class="m">
<xsl:text>?></xsl:text>
</SPAN>
</DIV>
</xsl:template>
<xsl:template match="processing-instruction('xml')">
<DIV class="e">
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">
<xsl:text>&lt;?</xsl:text>
</SPAN>
<SPAN class="pi">
<xsl:text>xml </xsl:text>
<xsl:for-each select="@*">
<xsl:value-of select="name(.)"/>
<xsl:text>="</xsl:text>
<xsl:value-of select="."/>
<xsl:text>" </xsl:text>
</xsl:for-each>
</SPAN>
<SPAN class="m">
<xsl:text>?></xsl:text>
</SPAN>
</DIV>
</xsl:template>
<xsl:template match="@*">
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*/@*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
</SPAN>
<SPAN class="m">="</SPAN>
<B>
<xsl:value-of select="."/>
</B>
<SPAN class="m">"</SPAN>
</xsl:template>
<xsl:template match="text()">
<DIV class="e">
<SPAN class="b"> </SPAN>
<SPAN class="tx">
<xsl:value-of select="."/>
</SPAN>
</DIV>
</xsl:template>
<xsl:template match="comment()">
<DIV class="k">
<SPAN>
<A STYLE="visibility:hidden" class="b" false"
<SPAN class="m">
<xsl:text>&lt;!--</xsl:text>
</SPAN>
</SPAN>
<SPAN class="ci" id="clean">
<PRE>
<xsl:value-of select="."/>
</PRE>
</SPAN>
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">
<xsl:text>--></xsl:text>
</SPAN>
<SCRIPT>f(clean);</SCRIPT>
</DIV>
</xsl:template>
<xsl:template match="*">
<DIV class="e">
<DIV STYLE="margin-left:1em;text-indent:-2em">
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">&lt;</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
<xsl:if test="@*">
<xsl:text> </xsl:text>
</xsl:if>
</SPAN>
<xsl:apply-templates select="@*"/>
<SPAN class="m">
<xsl:text>/></xsl:text>
</SPAN>
</DIV>
</DIV>
</xsl:template>
<xsl:template match="*[node()]">
<DIV class="e">
<DIV class="c">
<A class="b" href="#" false"
<SPAN class="m">&lt;</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
<xsl:if test="@*">
<xsl:text> </xsl:text>
</xsl:if>
</SPAN>
<xsl:apply-templates select="@*"/>
<SPAN class="m">
<xsl:text>></xsl:text>
</SPAN>
</DIV>
<DIV>
<xsl:apply-templates/>
<DIV>
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">
<xsl:text>&lt;/</xsl:text>
</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
</SPAN>
<SPAN class="m">
<xsl:text>></xsl:text>
</SPAN>
</DIV>
</DIV>
</DIV>
</xsl:template>
<xsl:template match="*[text() and not (comment() or processing-instruction())]">
<DIV class="e">
<DIV STYLE="margin-left:1em;text-indent:-2em">
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">
<xsl:text>&lt;</xsl:text>
</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
<xsl:if test="@*">
<xsl:text> </xsl:text>
</xsl:if>
</SPAN>
<xsl:apply-templates select="@*"/>
<SPAN class="m">
<xsl:text>></xsl:text>
</SPAN>
<SPAN class="tx">
<xsl:value-of select="."/>
</SPAN>
<SPAN class="m">&lt;/</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
</SPAN>
<SPAN class="m">
<xsl:text>></xsl:text>
</SPAN>
</DIV>
</DIV>
</xsl:template>
<xsl:template match="*[*]" priority="20">
<DIV class="e">
<DIV STYLE="margin-left:1em;text-indent:-2em" class="c">
<A class="b" href="#" false"
<SPAN class="m">&lt;</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
<xsl:if test="@*">
<xsl:text> </xsl:text>
</xsl:if>
</SPAN>
<xsl:apply-templates select="@*"/>
<SPAN class="m">
<xsl:text>></xsl:text>
</SPAN>
</DIV>
<DIV>
<xsl:apply-templates/>
<DIV>
<SPAN class="b">
<xsl:call-template name="entity-ref">
<xsl:with-param name="name">nbsp</xsl:with-param>
</xsl:call-template>
</SPAN>
<SPAN class="m">
<xsl:text>&lt;/</xsl:text>
</SPAN>
<SPAN>
<xsl:attribute name="class"><xsl:if test="xsl:*"><xsl:text>x</xsl:text></xsl:if><xsl:text>t</xsl:text></xsl:attribute>
<xsl:value-of select="name(.)"/>
</SPAN>
<SPAN class="m">
<xsl:text>></xsl:text>
</SPAN>
</DIV>
</DIV>
</DIV>
</xsl:template>
<xsl:template name="entity-ref">
<xsl:param name="name"/>
<xsl:text disable-output-escaping="yes">&amp;</xsl:text>
<xsl:value-of select="$name"/>
<xsl:text>;</xsl:text>
</xsl:template>
</xsl:stylesheet>

Так же можно попробовать поискать аналогичные default-схемы xsl-преобразований в Сhrome (chrome://global/content/xml/XMLPrettyPrint.xsl) и других броузерах

Желающие могут скачать пример обработки просмотра и форматирования произвольных XML-файлов (управляемые и обычные формы).

Ноо повторяю, что вся автоматизация отображения произволного XML сводится к двум строчкам текста 1С

Преобразование.ЗагрузитьИзСтроки(ТекстПреобразованияXSL);
XML2HTML = Преобразование.ПреобразоватьИзСтроки(ИсходныйТекстXML);

и использованию подходящей XSL-схемы, которую можно найти как в интернете, так и у себя на локальном компьютере.

20 Comments

  1. theshadowco

    (0) а такой вариант не подойдет?

    #Если НЕ ВебКлиент Тогда
    
    ВремФайл = ПолучитьИмяВременногоФайла(«xml»);
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.ОткрытьФайл(ВремФайл);
    ЗаписьXML.ЗаписатьБезОбработки(ТекстСообщения);
    ЗаписьXML.Закрыть();
    
    ОткрытьФорму(«ОбщаяФорма.ПросмотрHTML», Новый Структура(«ДанныеHTML, Заголовок», ВремФайл, «Просмотр текста сообщения»), ЭтотОбъект);
    
    #КонецЕсли
    
    
    

    Показать

    ПросмотрHTML — форма с реквизитом «ДанныеHTML» строкового типа и полем html на форме..

    Reply
  2. tarassov

    (1) theshadowco,

    нет. Это «масло масляное». Подали на вход текст XML в ЗаписьXML.ЗаписатьБезОбработки(ТекстСообщения) и с помощью ЗаписьXML.Закрыть() его же и записали. Ничего не изменилось. И не отобразилось

    Reply
  3. gaglo

    В статье сразу написано, что

    интернет-броузеры умеют отображать текст произвольных XML-документов

    Это — по-моему — и есть

    решение с минимальными затратами и желательно без программирования

    Описанный в статье способ позволяет открыть XML-файл непосредственно в 1С.

    Это его единственное преимущество перед открытием в браузере или есть еще достоинства, о коих я не догадался?

    Reply
  4. tarassov

    (3) gaglo, да, речь идет только о 1С

    Reply
  5. gaglo

    (4) Ясно. Тогда позволю себе отметить: в публикации infostart.ru/public/21664/ двухпальцовый reader_XML, кстати, перечисленной в списке предшественников, есть ссылка на внешний XML-редактор firstobject, маленький (624к), быстрый (файл в 55 мег открывается секунд за 5), портабельный и бесплатный. Да, и он еще позволяет редактировать!

    ЗЫ. А ход вашей мысли (то есть выдрать XSL из браузера и применить у себя) мне нравится!

    Reply
  6. tarassov

    (5) gaglo, Так понятно, что вне 1С есть тысяча и одна удабная утилита для работя с XML. Но ведь я специально указал область задачи: «Внутри 1С и без внешних библиотек». В этом случае все «внешние XML-редакторы» остаются «за бортом»

    Reply
  7. gaglo

    (6) А вот это мне и непонятно. Зачем специально было так сужать область задачи? Неужели «просто так»? Иными словами, зачем так часто стоит задача отображать XML именно в поле HTML-документа? Если мы хотим XML обработать, так вроде бы отображать и не надо… Навскидку придумал только случай, когда на входе валятся файлы самых разных структур, мы их показываем оператору и спрашиваем: «Чего с ним делать?» А оператор проглядит его, решает, что этот по алгоритму 15, а этот просто отклонить, и нажимает соответствующую кнопку. Но выглядит это неестественно.

    Reply
  8. tarassov

    (7) gaglo,

    У меня эта задача возникла из разработки интерфейса отслеживания почтовых отправлений различных служб доставки (СДЭК, Деловые линии, ДПД, ПЭК и т.п.) для интернет-магазина http://automediya.ru/, аналогично тому, как я сделал для Почты России http://infostart.ru/public/347510/

    Данные, полученные с с того или ниого вэб-сервиса при этом не требовалось обрабатывать, а только показывать, но в красивом и читаемом виде. Оказалось, что наиболее просто и естественно отобразить их как раз поле HTML-документа

    При этом потребовалось обрабатывать исключительные ситуации. А именно, когда код состояния ответа HTML не равен 200. Можно бы было выдать сообщение «КодСостояния=…», но в некотрых случаях в теле ответа приходит содезная информация. Например пришется что-то об ошибке сети, веб-сервиса, прокси и прочее. Вот для таких особых ситуаций универсальный просмотр как раз годится.

    Ну а мне еще удобно было им пользоваться при отладке, когда я, в точности как описано выше, имел дело с XML-файлами самих разных структур.

    Reply
  9. tarassov

    (5) gaglo,

    Если уж сравнивать с аналогами не по принципу соответствия поставленной задаче, а про универсальности и простоте, то мне больше чем «двупальцевый» нравится «однопальцевый» вариант http://infostart.ru/public/84254/

    Reply
  10. theshadowco

    (2) Отлично отображается, вы попробуйте :). Не первый год так работает, отображает любые XML.

    Как работает у меня:

    Есть регистр с реквизитом, содержащим текст XML. Иногда требуется посмотреть XML в более читабельном виде, чем просто строка. Для Этого на форме записи добавлена указанная в примере команда (точнее выдержка из команды), которая создает временный файл и показывает его в форме просмотре. В этой форме (управляемая) нет ничего интересного, кроме строкового реквизита, которому соответствует элемент формы с видом «Полле HTML документа»

    Reply
  11. tarassov

    (10) theshadowco,

    А что в вашем примере скрыто под конструкцией «ОбщаяФорма.ПросмотрHTML» ?

    Давайте вместе посмотрим, что там внутри в этой форме делается

    Reply
  12. tarassov

    (10) theshadowco,

    В этой форме (управляемая) нет ничего интересного

    Надо все-таки смотреть на устройство формы.

    По виду снимка вывода можно предположить, что используется OLE-объект

    Будет ли Ваша конструкция без переделок работать под Linux?

    Reply
  13. theshadowco

    (12) не используется ничего, только платформа.

    Смотрите скрины. Проще некуда.

    Reply
  14. tarassov

    (13) theshadowco,

    Может быть тогда что-то дополнительное установлено в настройках операционной системы и ее интернет-броузерах?

    Reply
  15. theshadowco

    (14) да нет, все по дефолту.

    Reply
  16. tarassov

    (15) theshadowco,

    Я наконец понял, что имелось в виду.

    Этот пример действительно работает. Но если все делать через файлы (ЗаписьXML.ОткрытьФайл() вместо ЗаписьXML.УстановитьСтроку()) и полю HTMLДокумента соответственно передавать на вход имя файла на диске, а не текст документа.

    Я же говорил о другом, пытался показывать принцип работы с текстом файла, т.е. строковые операции, без привлечения файловой системы

    Reply
  17. yuraer

    Похоже, что xsl не валидный:

    Ошибка разбора XML: — [296,23]

    Фатальная ошибка:

    Couldn’t find end of Start Tag A line 296

    Reply
  18. NicholasUzunov

    В статье приводиться default.xsl с ошибками. Пример

    <A STYLE=»visibility:hidden» class=»b» false»

    Тут тег <A> не закрыт. Рабочий XSL скачал от сюда https://gist.github.com/byrner75/5659000.

    А вот не много измененный код:

    Преобразование = Новый ПреобразованиеXSL;
    Преобразование.ЗагрузитьИзФайла(«D:defaultss.xsl»);
    ЭлементыФормы.ПолеHTMLДокумента1.УстановитьТекст(Преобразование.ПреобразоватьИзФайла(«D:WayBillAct.xml»));
    Reply
  19. maloy-v

    До сих пор актуально. Фишка с непосредственным подсовывание файла в форму HTML в последних релизах перестала работать.

    А «Преобразование» работает. Красота!

    Reply
  20. CagoBHuK

    Хотел поставить автору плюс, но не буду, т.к. XSL невалиден. Зато поставил плюс (18).

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *