Como manter espaços em branco em um arquivo XML com C#

Quando é necessário exibir ou salvar um arquivo XML com C#, o arquivo pode ser carregado totalmente formatado mais quando salva a formatação fica totalmente desconfigurada, assim ficando quase impossível identificar possíveis problemas em arquivos grandes.

Para manter essa formação com os espaços já definidos existe uma propriedade na classe XmlDocument para manter essas informações quando salvar a propriedade se chama PreserveWhitespace quando definido para false o arquivo mantem os espaços do arquivo existente, mais por padrão ela é definida como true.

Código exemplificando o que ocorre para uma aplicação console básica.


XmlDocument xmlDocument = new XmlDocument();
 xmlDocument.LoadXml(
 @"<teste>
 <a> teste </a>
 <b> </b>
 <c></c>
 <d> </d>
 </teste>");
 Console.WriteLine("Exemplo com PreserveWhitespace = false");
 xmlDocument.PreserveWhitespace = false;
 xmlDocument.Save(Console.Out);
 Console.WriteLine("");
 Console.WriteLine("");

Console.WriteLine("Exemplo com PreserveWhitespace = true");

xmlDocument.PreserveWhitespace = true;

 xmlDocument.Save(Console.Out);

Console.ReadKey();

Como descobrir o tamanho de um arquivo

Para descobrir o tamanho  de um arquivo com C# basta utilizar a classe FileInfo, e utilizar a propriedade Length que retorno o tamanho em bytes do arquivo, ai basta dividir por 1024 quantas vezes precisar para ter o tamanho na proporção que precisa, esse e um exemplo do calculo ate GB:

FileInfo fi =new FileInfo("nomeArquivo.doc");
 var TamanhoEmKb = (fi.Length/1024F);
 var TamanhoEmMb = ((fi.Length/1024F)/1024F);
 var TamanhoEmGb = (((fi.Length/1024F)/1024F)/1024F);

Como Salvar um classe em XML

Se você necessita criar um xml a partir de uma classe, e depois é necessário que ao carregar esse arquivo você conseguir recriar a classe, isso é bem simples apenas é necessário seguir esses passos.

Primeiro você precisa definir a classe que você quer que crie o XML como Serializable colocando o atributo em cima( [Serializable] ), após isso é necessário criar um construtor sem parâmetros, ele pode ser protected apenas não pode ser private, segue um exemplo de uma classe criada para gerar um XML:

[Serializable] // atributo que define que a classe é serializavel
 public class Pessoa
 {
 private string _nome;
 private string _nomeMae;
 private int _idade;

//Obrigatório ter esse construtor.
 protected Pessoa()
 {

 }

public Pessoa(string nome, string nomeMae, int idade)
 {
 _nome = nome;
 _nomeMae = nomeMae;
 _idade = idade;
 }

public string Nome
 {
 get { return _nome; }
 set { _nome = value; }
 }

public string NomeMae
 {
 get { return _nomeMae; }
 set { _nomeMae = value; }
 }

public int Idade
 {
 get { return _idade; }
 set { _idade = value; }
 }

public override string ToString()
 {
 return string.Format("Nome: {0} Minha mãe é:{1} e eu tenho {2}anos", Nome, NomeMae, Idade);
 }
 }

Após isso vamos a um exemplo de como salvar essa classe em um arquivo Físico em xml e depois carregado(Você também pode gerar apenas o Texto em xml utilizando a mesma estrutura, mas ao invés de Stream utilize TextWriter):

static void Main(string[] args)
 {

using (Stream fileStream = new FileStream(@"C:\pessoa.xml", FileMode.OpenOrCreate))
 {
 Pessoa pessoalParaCriarArquivo = new Pessoa("Meu Nome", "Nome da Minha Mae", 20);
 XmlSerializer xmlSerializer = new XmlSerializer(pessoalParaCriarArquivo.GetType());
 xmlSerializer.Serialize(fileStream, pessoalParaCriarArquivo);
 Console.Write("Pessoa Para Salvar Arquivo:" + Environment.NewLine + pessoalParaCriarArquivo.ToString() + Environment.NewLine);
 }

 using (XmlReader xmlReader = XmlReader.Create(@"C:\pessoa.xml"))
 {
 XmlSerializer xmlSerializer = new XmlSerializer(typeof(Pessoa));
 Pessoa pessoaLidaDoArquivo = (Pessoa) xmlSerializer.Deserialize(xmlReader);
 Console.Write("Pessoa lida do Arquivo:" + Environment.NewLine + pessoaLidaDoArquivo.ToString());
 }

Console.ReadKey();

}

Esse código irá gerar um arquivo xml no c:\ da sua máquina com a estrutura da classe, e após irá ler esse mesmo arquivo e criar um objeto igual ao que foi gerado o arquivo, essa transformação para xml é muito útil para transferência de informações entre Webservices ou até mesmo de uma aplicação para outra.

O arquivo seguindo esse exemplo passado irá ficar assim:

<?xml version="1.0"?>
<Pessoa xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Nome>Meu Nome</Nome>
<NomeMae>Nome da Minha Mae</NomeMae>
<Idade>20</Idade>
</Pessoa>

Como copiar o conteudo de uma Stream para outra

Em alguns casos é necessário copiar o conteudo de uma Stream para outra Stream. Se voce apenas atribuir para uma nova variavel não será criada uma nova variavel e sim ter mais uma referencia para a mesma variavel em memoria(se não conseguiu leia esse post).
Voltando para o nosso problema, para copiar o conteúdo de uma stream para outra efetuei a chamada para esse método passando a stream que contem o conteúdo que deseja copiar no parâmetro src e passe a stream que deseja receber esse conteúdo. Segue o método para utilizar:

public static void CopyTo(System.IO.Stream src, System.IO.Stream dest)
 {

int readCount;

var buffer = new byte[8192];
while ((readCount = src.Read(buffer, 0, buffer.Length)) != 0)
dest.Write(buffer, 0, readCount);
 }

Quando salva um arquivo ocorre o erro “Illegal characters in path.”

Quando uma aplicação precisa salvar um arquivo, ou criar uma pasta que o usuário define o nome ou depende de algo que um usuário preenche é necessário um certo cuidado com esses dados pois dependendo da informação que o usuário coloca pode ocorrer o erro “Illegal characters in path.”.

Para evitar isso é necessário toda vez que for salvar um arquivo executar uma rotina parecida com essa:

public static string RemoverCaracteresInvalidosArquivo(string path)
 {

if (path.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) >= 0)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
path = path.Replace(c.ToString(), string.Empty);
}
}

return path;

}

Neste caso é tirado esse carácter e substituído por uma string vazia, mas é possível fazer trocando os valores por um valor qualquer.

Copia de Stream não está validando com Hash MD5

Eu sei que o titulo do post não é auto-explicativo, por isso vou explicar o real problema que ocorreu:
Eu precisava transferir um arquivo grande via WCF, buscando na internet decidi fazer a transferencia desse arquivo por partes( enviando o arquivo em pedaços, cada requisição seria certa de 1MB), após o termino do envio do arquivo eu precisava gerar um Hash MD5 do meu arquivo no cliente e um hash no servidor para comparar, porém esse hash nunca era igual.

Bom após me debater muito pensando que era algum problema do WCF decidi fazer uma aplicação de testes bem rapida e percebi que não era problema do WCF e sim da copia do arquivo que eu estava criando, o código que eu estava utilizando para efetuar a copia do arquivo era o seguinte:

StreamReader stream = new StreamReader(textBox1.Text);
 using (FileStream stream2 = File.Create(textBox2.Text))
 {
var bytes = default(byte[]);
var buffer = new byte[10000];
       var bytesRead = default(int);
       while ((bytesRead = stream.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
       {
             stream2.Write(buffer, 0, <span style="text-decoration: underline;"><strong>buffer.Count()</strong></span>);

}
 }

Porém quando gerava o Hash do arquivo em MD5 e comparava com original sempre era diferente ao percebi que o buffer.Count() nem sempre seria a quantidade de bytes a ser gravado no arquivo, e possivelmente era por isso não deu outra apenas uma uma alteração consegui resolver meu problema, segue como ficou o código arrumado.


StreamReader stream = new StreamReader(textBox1.Text);

using (FileStream stream2 = File.Create(textBox2.Text))
 {
      var bytes = default(byte[]);
      var buffer = new byte[10000];
      var bytesRead = default(int);
      while ((bytesRead = stream.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
      {
           stream2.Write(buffer, 0, bytesRead);

}
 }