jueves, 30 de diciembre de 2010

Editar un ResX

Una vez me enfrenté a un reto interesante. En efecto no sabía cómo resolver el problema, pero estaba seguro que no podía ser muy difícil. En realidad no lo fue. La cuestión que necesitaba era editar un imagelist que se encontraba serializado en un archivo de recursos (ResX). Lo bueno es que ésto puede servir para cualquier tipo de objeto que se encuentre serializado en un archivo de recursos, y aunque voy a hacer el ejemplo con el imagelist, tengan presente que se puede hacer con cualquier objeto.

La primera cosa que se necesita saber es que un archivo de recursos no es otra cosa más que un archivo XML (En este caso estoy rosando peligrosamente lo obvio). El primer paso entonces es cargar el archivo ResX en un documento Xml.


La estructura del xml no es muy compleja, por lo que es fácil deducir qué información necesitamos y cómo extraerla. Usando XPATH hacemos una consulta sobre el documento XML para obtener el item que deseamos modificar o deserializar. En este caso es un nodo value, hijo de un nodo data cuyo attributo name sea imgLstLogos.ImageStream. Luego extraemos el texto del nodo que es la representación del objeto.


Ahora a lo que vinimos. El nodo con el que estamos trabajando es algo similar a la siguiente imágen (click para agrandar):


Como pueden observar, el formato en que está serializado el objeto no es binario. Hasta cierto punto si lo es, pero es un tipo especial de binario. El formato que se utiliza es un string base 64, que se forma con caracteres ascii. Pueden ver más información técnica al respecto en Wikipedia. Pero hay un truco, dado que en el xml el string está dividido en bloques de 80 caracteres, tendremos que reemplazar con expresiones regulares algunos caracteres como fin de linea y espacios, a fin de tener la hilera de caracteres en una sola línea y un solo bloque.



Ya ahora sí podemos convertir la hilera en un arreglo de bytes, y utilizando las clases MemoryStream y BinaryFormatter, podemos obtener el objeto y castearlo. Para el caso del imagelist, lo que se guarda realmente es un ImageListStreamer. Basta con crear una nueva instancia de la clase ImageList y luego asignar la propiedad ImageStream con el valor deserializado para poder trabajar con las imágenes que estaban almacenadas.



Fácilmente podemos hacer modificaciones al objeto a nuestro antojo y posteriormente volverlo a encriptar. Siempre debemos tomar en cuenta que el objeto se debe guardar en string base 64 y en bloques de 80 caracteres. Como ejemplo vamos a eliminar la primera imagen del ImageList:



Luego vamos a convertir el objeto a su representación en binario. Para eso volvemos a usar las clases MemoryStream y el BinaryFormatter.



Ahora hay que convertirlo a su representación en String Base 64:



Como resultado obtenemos un arreglo de caracteres que es justo lo que necesitamos. Ahora basta con dividirlo en bloques de 80 caracteres. Por razones de eficiencia, utilizamos un StringBuilder, que se supone que es un poco mejor que concatenar strings por si solos (Dado que si se hacen concatenaciones, se crea un nuevo string y se desechan los dos anteriores).



Por último, cambiamos el valor del Texto en el nodo de donde originalmente lo tomamos y ya tenemos el documento Xml que corresponde al archivo de recursos actualizado. Nada más hay que recordar guardarlo, ya sea en el archivo original o en otra ruta distinta.



La mayoría del tiempo no es necesario editar un archivo de recursos de esta forma, ni mucho menos generarlo (Se puede usar la herramienta ResGen que viene con visual studio). Pero para el problema particular que tenía que afrontar era necesario. Dejo aquí el código más como referencia futura para mí mismo, pero si a alguien le funciona, es un plus.

No hay comentarios:

Publicar un comentario