Escrito por

Artigo Evandro Wendt · jan 16 3m read

Minificando XML no IRIS

Em um projeto em que estou trabalhando, precisamos armazenar alguns XMLs arbitrários no banco de dados. Esse XML não tem nenhuma classe correspondente no IRIS; precisamos apenas armazená-lo como uma string (ele é relativamente pequeno e cabe em uma string).
Como existem MUITOS (milhões!) de registros no banco de dados, decidi reduzir o tamanho o máximo possível sem usar compressão. Sei que parte do XML a ser armazenado está indentada, parte não, isso varia.

Para reduzir o tamanho, decidi minificar o XML, mas como minificar um documento XML no IRIS?
Pesquisei em todas as classes e utilitários e não encontrei nenhum código ou método pronto, então tive que implementar por conta própria, e acabou sendo bem simples no IRIS usando a classe %XML.TextReader, sinceramente, mais simples do que eu esperava.

Como isso pode ser útil em outros contextos, decidi compartilhar esse pequeno utilitário com a Comunidade de Desenvolvedores.
Testei com alguns documentos XML relativamente complexos e funcionou bem, segue o código.

/// Minify an XML document passed in the XmlIn Stream, the minified XML is returned in XmlOut Stream/// If XmlOut Stream is passed, then the minified XML is stored in the passed Stream, otherwise a %Stream.TmpCharacter in returned in XmlOut./// Collapse = 1 (default), empty elements are collapsed, e.g. <tag></tag> is returned as <tag/>/// ExcludeComments = 1 (default), comments are not returned in the minified XMLClassMethod MinifyXML(XmlIn As%Stream, ByRef XmlOut As%Stream = "", Collapse As%Boolean = 1, ExcludeComments As%Boolean = 1) As%Status
{
	#Include %occSAXSet sc=$$$OKTry {
		Set Mask=$$$SAXSTARTELEMENT+$$$SAXENDELEMENT+$$$SAXCHARACTERS+$$$SAXCOMMENTSet sc=##class(%XML.TextReader).ParseStream(XmlIn,.reader,,$$$SAXNOVALIDATION,Mask)
		#dim reader as%XML.TextReaderIf$$$ISERR(sc) QuitIf '$IsObject(XmlOut) {
			Set XmlOut=##class(%Stream.TmpCharacter).%New()
		}
		While reader.Read() {
			Set type=reader.NodeType
			If ((type="error")||(type="fatalerror")) {
				Set sc=$$$ERROR($$$GeneralError,"Error loading XML "_type_"-"_reader.Value)
				Quit
			}
			If type="element" {
				Do XmlOut.Write("<"_reader.Name)
				If Collapse && reader.IsEmptyElement {
					; collapse empty elementDo XmlOut.Write("/>")
					Set ElementEnded=1
				} Else {
					; add attributesFork=1:1:reader.AttributeCount {
						Do reader.MoveToAttributeIndex(k)
						Do XmlOut.Write(" "_reader.Name_"="""_reader.Value_"""")
					}
					Do XmlOut.Write(">")
				}
			} ElseIf type="chars" {
				Set val=reader.Value
				Do XmlOut.Write($select((val["<")||(val[">")||(val["&"):"<![CDATA["_$replace(val,"]]>","]]]]><![CDATA[>")_"]]>",1:val))
			} ElseIf type="endelement" {
				If$g(ElementEnded) {
					; ended by collapsingSet ElementEnded=0
				} Else {
					Do XmlOut.Write("</"_reader.Name_">")
				}
			} ElseIf 'ExcludeComments && (type="comment") {
				Do XmlOut.Write("<!--"_reader.Value_"-->")
			}
		}
	} Catch CatchError {
		#dim CatchError as%Exception.SystemExceptionSet sc=CatchError.AsStatus()
	}
	Quit sc
}

P.S.: Alguém sabe se existe outra forma mais simples de minificar XML no IRIS?