Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

Wednesday, 31 March 2010

Protocol Buffer

This article is related to serialization using protobuf a Google's data interchange format. I've been using XML serialization for several years in my Delphi win32 applications, and I was looking for a nicer system. Then I discovered this interesting system that is able to serialize / deserialize objects in a plain format, more human readable instead of the cumbersome XML format. It's a pity that the library isn't implemented on Delphi, but at least there are a lot of programming languages where the library is now implemented. You can find a list here. For testing the power of the library I decided to give it a go in a Java project. This will help me to brush up my Java skills and go further with a protobuf implementation. But one of the primary points behind Google Protocol Buffers is to have a more efficient data packet. Their particular approach uses a stream of binary data rather than the text based on XML. This makes for much less data being sent around and for a much faster parsing. My example will show you the general idea, the easy to build and use, the quick parse and the simplicity of the project.
To some extend I think that the idea is very good, but I'll wait changing my XML serialization until there is a Delphi release of the Protocol Buffer (I'm crossing my fingers). Anyway here you can see a simple example defining the ".proto" file, and the generation of the parser class.

Graph.proto:

package Graphs;

option java_package = "example.graph";
option java_outer_classname = "Graph";

message Node {
  required string id = 1;
  required int32 weight = 2;
}

message Edge {
  required string id = 1;
  required int32 weight = 2;
  required Node source = 3;
  required Node target = 4;
}


For generating the parser class, we need to download the last protobuf compiler (for win32):

C:\protoc-2.3.0-win32>protoc Graph.proto --java_out=C:\temp

After that, we'll get the Graph.java class that will help us serializing our objects. I've created a Serializator class that will handle the node class and will serialize it using the protobuf class:

node.java:

package example.graph;

public class node {
 private String id;
 private int weight;
 
 public node(String id, int weight) {
  setId(id);
  setWeight(weight);
 }

 public void setId(String id) {
  this.id = id;
 }

 public String getId() {
  return id;
 }

 public void setWeight(int weight) {
  this.weight = weight;
 }

 public int getWeight() {
  return weight;
 }
 
 public String toString() {
  return "Id: " + id + " Weight: " + String.valueOf(weight);
 }
}

Serializator.java:

package example.graph;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import example.graph.Graph.Node;

public class Serializator {
 public void SerializeNode(node n) throws IOException {
  FileOutputStream output = new FileOutputStream("node" + String.valueOf(n.getId()));
  
  Node.Builder nodeS = Node.newBuilder();
  nodeS.setId(n.getId());
  nodeS.setWeight(n.getWeight());
  nodeS.build().writeTo(output); 
 }
 
 public node DeserializeNode(int nodeId) throws FileNotFoundException, IOException {
  Node nodeD = Node.parseFrom(new FileInputStream("node" + String.valueOf(nodeId)));
  node n = new node(nodeD.getId(), nodeD.getWeight());
  return n;
 }
}

exampleGraphMain.java:

package example.graph;

import java.io.IOException;

public class exampleGraphMain {

 /**
  * @param args
  * @throws IOException 
  */
 public static void main(String[] args) throws IOException {

  //Serialization (Creating Nodes)
  node n = new node("1", 3);
  node n2 = new node("2", 10);
  
  System.out.println(n.toString());
  System.out.println(n2.toString());
  
  Serializator sr = new Serializator();
  sr.SerializeNode(n);
  sr.SerializeNode(n2);
  
  //Deserialization (Reconstructing Nodes)
  node n3 = sr.DeserializeNode(1);
  node n4 = sr.DeserializeNode(2);

  System.out.println(n3.toString());
  System.out.println(n4.toString());  
 }
}


If you see the generated files "node1 and node2" you'll see them in a byte code format.

Enjoy the reading!.

Thursday, 20 August 2009

Obtener la lista de feeds de tu Blog con Delphi

En éste artículo os mostraré como recuperar el fichero rss de vuestro blog y mostrar las últimas entradas de éste en un programa. Para ésto utilizaré varios ejemplos que he encontrado por la red, y unos de los que más me ha gustado y que está muy bien explicado es el de Zarco Gajic en About.com llamado Reading and manipulating XML Files (RSS) with delphi. En su ejemplo manipula su feed para descargarlo en local y mediante el TXMLDocument recorrer los nodos del documento y visualizarlo todo en un TListView.
Al ser usuario de Blogger, las cosas son un poco más complicadas aquí. Blogger dispone (que yo sepa) de suscripción a la página mediante rss y atom. Éstos dos ficheros los podemos visualizar si nos dirigimos a la dirección:
El problema que tenemos aquí es que los feeds se publican en formato Atom 1.0, y nosostros necesitamos un formato RSS 2.0 para poder leer la estructura del XML. ¿Como solucionamos ésto?. Pues bien, simplemente tenemos que utilizar el comando &alt=rss después de la URL de feed. De ésta manera tendremos un fichero con el formato deseado.

La ruta es la siguiente:
Ahora bien, desde la aplicación que ha creado Zarco Gajic y haciendo unas cuantas modificaciones, podremos leer la lista de nuestros feeds:


Podéis descargar la aplicación Thundax RSS Reader y estar a la última con mis publicaciones!

  • Enlaces de interés:
Reading and manipulating XML Files with delphi.
Display XML Feed in Delphi TreeView.
Ayudas para Blogger.


Saturday, 25 April 2009

Implementación de persistencia de objetos en delphi mediante XML

Delphi no dispone de muchos sistemas para no decir ninguno que implemente la persistencia de Objetos como Java por ejemplo. Y como dice el pensamiento pragmático: no intentes diseccionar una rana, constrúyete tú una de nueva!. Pues allá vamos!.

Lo que he hecho, es crear las clases, y estas implementan unos métodos WriteToXML y ReadFromXML que guardan su contenido en un fichero XML. Por lo tanto cada instancia de clase, guarda todo su contenido en el fichero que se le pasa, y luego también puede recuperar todo su contenido a través de este.

Aquí os dejo un ejemplo bastante completo de mi última aplicación ThundaxSaveSeries, que permite guardar listas de series (Lost, Anatomia de Grey, 24h, Big bang, etc.) y queráis tener un pequeño registro donde indicar la temporada del capítulo, si lo habéis descargado, si lo habéis visto, etc. A mi me va bastante bien, ya que es mas portable que un excel.


En este ejemplo podéis ver como hago las llamadas a IXMLDocument, y accedo a sus nodos mediante IXMLNode.

unit Lib_Series;

interface

uses
Contnrs, SysUtils, XMLDoc, XMLIntf;

type
TEstado = (Vista, Descargada, Pendiente);

TCapitulo = class(TObject)
numero: integer;
Temporada: integer;
estado: TEstado;
constructor Create(temporada: integer; numero: integer; estado: TEstado);
function WriteToXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode; sNode: string): IXMLNode;
function ReadFromXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode): IXMLNode;
end;

TSerie = class(TObject)
Nombre: string;
NumCapitulos: integer;
CapituloInicial: integer;
temporada: integer;
TTemporada: TObjectList;
completa: boolean;
constructor Create(Nombre: string; Temporada: integer; NumCapitulos: integer; tieneCapituloZero: boolean);
function WriteToXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode; sNode: string): IXMLNode;
function ReadFromXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode; sNode: string): IXMLNode;
end;

TListaSeries = class(TObject)
ListaSeries: TObjectList;
constructor Create();
destructor Destroy(); override;
procedure WriteToXML(sArchivo: string);
procedure ReadFromXML(sArchivo: string);
end;

implementation

{ TListaSeries }

procedure TListaSeries.ReadFromXML(sArchivo: string);
var
Document: IXMLDocument;
iXMLRootNode, iXMLRootNode2: IXMLNode;
objSerie: TSerie;
begin
Document := TXMLDocument.Create(nil);
try
Document.LoadFromFile(sArchivo);
iXMLRootNode := Document.ChildNodes.First;
iXMLRootNode2 := iXMLRootNode.ChildNodes.first;
while iXMLRootNode2 <> nil do
begin
if iXMLRootNode2.NodeName = 'Serie' then
begin
objSerie := TSerie.CreateEmpty();
objSerie.ReadFromXML(Document, iXMLRootNode2, 'Serie');
Self.ListaSeries.Add(objSerie);
end;
iXMLRootNode2 := iXMLRootNode2.NextSibling;
end;
finally
Document := nil;
end;
end;

procedure TListaSeries.WriteToXML(sArchivo: string);
var
XMLDoc: TXMLDocument;
iNode, NodeSeccio: IXMLNode;
i: integer;
objSerie: TSerie;
begin
XMLDoc := TXMLDocument.Create(nil);
XMLDoc.Active := True;
iNode := XMLDoc.AddChild('TListaSeries');
for i := 0 to ListaSeries.count - 1 do
begin
NodeSeccio := iNode.AddChild('Serie');
objSerie := TSerie(ListaSeries[i]);
if objSerie <> nil then
objSerie.WriteToXML(XMLDoc, NodeSeccio, 'Serie');
end;
XMLDoc.SaveToFile(sArchivo);
end;

function TCapitulo.WriteToXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode; sNode: string): IXMLNode;
begin
iXMLRootNode.attributes['numero'] := IntToStr(Self.numero);
iXMLRootNode.attributes['Temporada'] := IntToStr(Self.Temporada);
iXMLRootNode.attributes['estado'] := TEstadoToStr(Self.estado);
end;

function TCapitulo.ReadFromXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode): IXMLNode;
begin
Self.numero := StrToInt(iXMLRootNode.attributes['numero']);
Self.Temporada := StrToInt(iXMLRootNode.attributes['Temporada']);
Self.estado := StrToTEstado(iXMLRootNode.attributes['estado']);
end;

function TSerie.WriteToXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode; sNode: string): IXMLNode;
var
iNodoObjeto: IXMLNode;
i: integer;
begin
iXMLRootNode.attributes['Nombre'] := Self.Nombre;
iXMLRootNode.attributes['NumCapitulos'] := IntToStr(Self.NumCapitulos);
iXMLRootNode.attributes['CapituloInicial'] := IntToStr(Self.CapituloInicial);
iXMLRootNode.attributes['temporada'] := IntToStr(Self.temporada);
iXMLRootNode.attributes['completa'] := BoolToStr(Self.completa);
for i := 0 to Self.TTemporada.Count - 1 do
begin
iNodoObjeto := iXMLRootNode.AddChild('Capitulos');
TCapitulo(Self.TTemporada.Items[i]).GuardarEnXML(XMLDoc, iNodoObjeto, 'Capitulos');
end;
end;

function TSerie.ReadFromXML(XMLDoc: IXMLDocument; iXMLRootNode: IXMLNode; sNode: string): IXMLNode;
var
iNodoObjeto, iNodoAtributo: IXMLNode;
objCapitulo: TCapitulo;
begin
iNodoObjeto := iXMLRootNode;
if iNodoObjeto.nodeName = sNode then
begin
Self.Nombre := iNodoObjeto.attributes['Nombre'];
Self.NumCapitulos := StrToInt(iNodoObjeto.attributes['NumCapitulos']);
Self.CapituloInicial := StrToInt(iNodoObjeto.attributes['CapituloInicial']);
Self.temporada := StrToInt(iNodoObjeto.attributes['temporada']);
Self.completa := StrToBool(iNodoObjeto.attributes['completa']);
iNodoAtributo := iNodoObjeto.ChildNodes.first;
while iNodoAtributo <> nil do
begin
objCapitulo := TCapitulo.CreateEmpty();
objCapitulo.ReadFromXMLDoc(XMLDoc, iNodoAtributo);
self.TTemporada.Add(objCapitulo);
iNodoAtributo := iNodoAtributo.nextSibling;
end;
end;
end;

end.








La persistencia de los objetos mediante su XML:


  • Enlaces de interés:

Podemos encontrar proyectos Open Source, que intentar hacer la persistencia de objetos para delphi, uno de estos es "Delphi Persistent Object" y lo podemos encontrar en SourceForge. Aquí os dejo el enlace directo:
http://sourceforge.net/projects/depo/