InfoPath como aplicación es capaz de generar documentos XML que contienen no solo la información también la estructura del mismo.
Utilizando la herramienta XSD.exe es posible generar una clase que facilite manipular dicha información (http://msdn.microsoft.com/en-us/library/bb251017.aspx). Sin embargo, es específica para cada documento.
En muchas oportunidades sucede que se requiere codificar (por ejemplo un flujo de trabajo), para múltiples documentos y formatos.
Para facilitar estas actividades, se me ocurrió definir una clase que administre genéricamente el contenido de documentos InfoPath.
Esta clase recibe un elemento de una biblioteca de documentos InfoPath de SharePoint y carga el documento en memoria para facilitar su manipulación.
La definición de la clase.
Comencemos con la lista de importaciones y variables necesarias.
Imports System.Xml.Serialization
Imports System.Xml
”’ <summary>
”’ Permite manipular el contenido de un documento de InfoPath en algo fácilmente accesible por código
”’ </summary>
”’ <remarks>Carga el contenido del documento, adjunto a una biblioteca de documentos de MOSS
”’ y crea listas fácilmente accesibles tanto de los atributos como de los valores (elementos)
”’ </remarks>
Public Class InfoPathConverter
”’ <summary>
”’ Documento conteniendo todo el documento InfoPath
”’ </summary>
”’ <remarks></remarks>
Dim xDoc As XDocument = Nothing
”’ <summary>
”’ Lista de atributos
”’ </summary>
”’ <remarks></remarks>
Dim atrs As Dictionary(Of String, XAttribute)
”’ <summary>
”’ lista de valores (elementos)
”’ </summary>
”’ <remarks></remarks>
Dim els As Dictionary(Of String, XElement)
Una de las características que me suelen ser necesarias es la de asegurarme que existan ciertas propiedades y atributos en el documento ya que son utilizados en la lógica del flujo.
Por ello, la clase expone 2 propiedades para contener los atributos y los campos que son requeridos.
”’ <summary>
”’ Lista de nombres de atributos requeridos en el documento
”’ </summary>
”’ <remarks></remarks>
Private mAtributes As List(Of String)
”’ <summary>
”’ Define que atributos son necesarios en el documento para considerarlo válido
”’ </summary>
”’ <value></value>
”’ <returns></returns>
”’ <remarks></remarks>
Public ReadOnly Property AttributesNeeded() As List(Of String)
Get
If mAtributes Is Nothing Then
mAtributes = New List(Of String)
mAtributes.Add("Etapa")
End If
Return mAtributes
End Get
End Property
Private mFieldsNeeded As List(Of String)
”’ <summary>
”’ Define que campos son necesarios en el documento para considerarlo válido
”’ </summary>
”’ <value></value>
”’ <returns></returns>
”’ <remarks></remarks>
Public ReadOnly Property FieldsNeeded() As List(Of String)
Get
If mFieldsNeeded Is Nothing Then
mFieldsNeeded = New List(Of String)
End If
Return mFieldsNeeded
End Get
End Property
Cargando el documento en memoria.
El método getDocumentContent recibe entonces el item de la biblioteca de documentos, obtiene el documento XML y carga en diccionarios los atributos y propiedades
”’ <summary>
”’ Carga en las listas el contenido del documento InfoPath
”’ </summary>
”’ <param name="item">Item de la biblioteca de docuemntos, conteniendo el mismo</param>
”’ <remarks>Carga el contenido del documento, adjunto a una biblioteca de documentos de MOSS
”’ y crea listas fácilmente accesibles tanto de los atributos como de los valores (elementos)
”’ </remarks>
Sub getDocumentContent(ByVal item As SPListItem)
‘La propiedad File expone el documento InfoPath
Dim xtextr As New XmlTextReader(item.File.OpenBinaryStream)
‘Se carga entonces el documento desde el Stream
xDoc = XDocument.Load(xtextr)
‘Obtiene los atributos
atrs = (From x As XAttribute In xDoc.Root.Attributes).ToDictionary( _
Of String)(Function(x) x.Name.LocalName.ToUpper)
‘ Evalúa si todos los atributos requeridos están pesentes
For Each s As String In AttributesNeeded
Dim lookFor As String = s.ToUpper
If (From a As XAttribute In atrs.Values _
Where a.Name.LocalName.ToUpper = lookFor).ToArray.Length < 1 Then
Throw New Exception("Formulario inválido: le falta el atributo " & s)
End If
Next
‘ Evalúa lo mismo para los campos (Elements)
els = (From v As XElement In xDoc.Root.Elements).ToDictionary( _
Of String)(Function(v) v.Name.LocalName.ToUpper)
For Each s As String In FieldsNeeded
Dim lookFor As String = s.ToUpper
If (From a As XElement In els.Values _
Where a.Name.LocalName.ToUpper = lookFor).ToArray.Length < 1 Then
Throw New Exception("Formulario inválido: le falta el campo " & s)
End If
Next
End Sub
Teniendo el documento en memoria, se pueden exponer los valores y asignarles nuevos con estos métodos:
”’ <summary>
”’ Cambia el valor de un atributo
”’ </summary>
”’ <param name="what">Nombre del atributo</param>
”’ <param name="newValue">Nuevo Valor</param>
”’ <remarks></remarks>
Sub ChangeAttribute(ByVal what As String, ByVal newValue As String)
If atrs.ContainsKey(what.ToUpper) Then
atrs(what.ToUpper).Value = newValue
End If
End Sub
”’ <summary>
”’ Obtiene el valor de uhn atributo
”’ </summary>
”’ <param name="what">Nombre del atirbuto</param>
”’ <returns>Contenido del mismo (siempr como cadena de caracteres) o valor nulo si no existe
”’ </returns>
”’ <remarks></remarks>
Function ReadAttribute(ByVal what As String) As String
If atrs.ContainsKey(what.ToUpper) Then
Return atrs(what.ToUpper).Value
Else
Return Nothing
End If
End Function
”’ <summary>
”’ Cambia el valor de un elemento
”’ </summary>
”’ <param name="what">Nombre del elemento</param>
”’ <param name="newValue">Valor a asignar</param>
”’ <remarks></remarks>
Sub ChangeValue(ByVal what As String, ByVal newValue As String)
If els.ContainsKey(what.ToUpper) Then
els(what.ToUpper).Value = newValue
End If
End Sub
”’ <summary>
”’ Obtiene le valor de un elemento
”’ </summary>
”’ <param name="what">Nombre del elemento a leer</param>
”’ <returns>Valor del elemento (siempre como cadena de caracteres), o valor nulo si no existe
”’ </returns>
”’ <remarks></remarks>
Function ReadValue(ByVal what As String) As String
If els.ContainsKey(what.ToUpper) Then
Return els(what.ToUpper).Value
Else
Throw New KeyNotFoundException("El campo " & what & " no se encuentra en el documento")
End If
End Function
Guardando cambios.
Finalmente ento0nces, con un método Save, se puede guardar en la lista de origen… o en otra. Para ello, se le envía al método el ListItem que contendrá el documento.
”’ <summary>
”’ Guarda el contenido de los valores y atributos en el documento InfoPath actualizando los mismos
”’ </summary>
”’ <param name="item"></param>
”’ <remarks></remarks>
Public Sub Save(ByVal item As SPListItem)
item.File.CheckOut()
Dim memoryStream As New IO.MemoryStream()
Dim sett As New XmlWriterSettings
sett.Encoding = System.Text.Encoding.UTF8
sett.OmitXmlDeclaration = False
Dim xmlTextWriter As XmlWriter = XmlWriter.Create(memoryStream, sett)
xDoc.Save(xmlTextWriter)
xmlTextWriter.Flush()
memoryStream.Position = 0
item.File.SaveBinary(memoryStream.GetBuffer)
item.File.CheckIn("")
End Sub
Si si, ya sé. quieres verlo completo. Pues aquí está la clase entera. https://blogvisionarios.com/ES/dseara/Archivos/InfoPathConverter.zip