Generate xml through a class, even without value in the property

3

I'm generating an xml through an existing class, for example

[XmlRoot("pessoa")]
public class pessoa
{
    //[CPFValidation]
    [XmlElement("cpf")]
    public virtual string cpf { get; set; }

    [XmlElement("nome")]
    public virtual string nome{ get; set; }

    [XmlElement("telefone")]
    public virtual string telefone{ get; set; }
}

And I'm using the following code to generate xml

        public static string CreateXML(Object obj)
    {
        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());

        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Encoding = new UnicodeEncoding(false, false); 
        settings.Indent = true;
        settings.OmitXmlDeclaration = true;

        using (StringWriter textWriter = new StringWriter())
        {
            using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
            {
                serializer.Serialize(xmlWriter, obj);
            }
            return textWriter.ToString(); 
        }
    }

Seeing that there are 3 properties: cpf, name and phone

When I do not assign value to one of them, it gets null

When generating the xml it does not generate the element

I would like it to generate even though it is empty or null

If I fill out var test = new Person { cpf="123", name="so and so"} and have it generated, it only generates

    <cpf>123</cpf>
<nome>fulano</nome>


E não gera o elemento <telefone></telefone>

So I used an anottation to set the values for me ... srsrsrsrs

public class XMLAtributos : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null) return false;
            IList<PropertyInfo> propriedades = new List<PropertyInfo>(value.GetType().GetProperties());

            foreach (PropertyInfo propriedade in propriedades)
            {
                object valor = propriedade.GetValue(value, null);
                if (valor == null)
                {
                    propriedade.SetValue(value, " " ,null);
                }
            }
            return true;
        }
    }
    
asked by anonymous 21.02.2014 / 18:11

4 answers

0

You can create a class that inherits from Xml.XmlTextWriter and override the WriteEndElement method, I did this in an NF-e component, where the entire element needs to be created.

Imports System.IO
Public Class FullEndingXmlTextWritter
   Inherits Xml.XmlTextWriter

    Public Sub New(w As TextWriter)
        MyBase.New(w)
    End Sub

    Public Sub New(w As IO.Stream, encoding As System.Text.Encoding)
        MyBase.New(w, encoding)
    End Sub

    Public Sub New(fileName As String, encoding As System.Text.Encoding)
        MyBase.New(fileName, encoding)
    End Sub

    Public Overrides Sub WriteEndElement()
        MyBase.WriteFullEndElement()  'Aqui está a mágica.
    End Sub
End Class

At the time of serialization use this class:

serializer.Serialize(new FullEndingXmlTextWritter(), obj);
    
25.02.2014 / 19:02
1

Tell the serializer that this element can be serialized null (empty tag)

[XmlRoot("pessoa")]
public class pessoa
{
    //[CPFValidation]
    [XmlElement("cpf", IsNullable = true)]
    public virtual string cpf { get; set; }

    [XmlElement("nome", IsNullable = true)]
    public virtual string nome{ get; set; }

    [XmlElement("telefone", IsNullable = true)]
    public virtual string telefone{ get; set; }
}

If you have described your problem correctly, it should resolve.

There are more parameters that can be passed to customize your serialization see the documentation here

    
21.02.2014 / 18:18
0

You must set the property to be saved as an empty string "" , instead of null . So the value containing the empty string will appear in the xml.

EDIT

Try to create the following methods in your class along with their properties:

public bool ShouldSerializecpf() {return true;}
public bool ShouldSerializenome() {return true;}
public bool ShouldSerializetelefone() {return true;}

Maybe this will solve, but I'm not sure ... I'll test and edit.

But still, I should point out that if it works, when you load the xml value, the value null will possibly be changed to an empty string.

EDIT 2 It does not work, but you can work with gambiarra below. = \

Make your class pessoa look like this:

[XmlRoot("pessoa")]
public class pessoa
{
    //[CPFValidation]
    [XmlElement("cpf")]
    public virtual string cpf { get; set; }

    [XmlElement("nome")]
    public virtual string nome { get; set; }

    [XmlElement("telefone")]
    public virtual string telefone { get; set; }

    public bool ShouldSerializecpf()
    {
        this.cpf = this.cpf ?? "";
        return true;
    }
    public bool ShouldSerializenome()
    {
        this.nome = this.nome ?? "";
        return true;
    }
    public bool ShouldSerializetelefone()
    {
        this.telefone = this.telefone ?? "";
        return true;
    }
}

Now it will work, but this is not a very cool thing to do ... after all it is a gambiarra.

EDIT 3 There is no way to do otherwise, that is only using XmlSerializer ... I saw in the source code, when the value is null, it will write xsi:nil="true" if IsNullable is set ... or this, or nothing.

Obviously, we can do post-processing in the generated xml text ... we could for example use a regex, but this is not very recommendable, even more so to solve a problem that seems to have no implication :

xml = Regex.Replace(
    xml,
    @"<\s*([\w\-]*?(?:\s*\:\s*[\w\-]*?)?)\s*xsi\:nil\=""true""\s*/>",
    "<$1></$1>");

Then you only have to add IsNullable = true to the attributes XmlElement of your properties, and soon after generating the xml use the substitution by regex.

But then do not go complaining if people curse you for using regex with xml ... hehe!

    
21.02.2014 / 18:22
0

You can use the XmlOutput library. An XML written on it looks like this:

XmlOutput xo = new XmlOutput()
    .XmlDeclaration()
        .Node("root").Within()
            .Node("user").Within()
                .Node("username").InnerText("orca")
                .Node("realname").InnerText("Mark S. Rasmussen")
                .Node("description").InnerText("I'll handle any escaping (like < & > for example) needs automagically.")
                .Node("articles").Within()
                    .Node("article").Attribute("id", "25").InnerText("Handling DBNulls")
                    .Node("article").Attribute("id", "26").InnerText("Accessing my privates")
            .EndWithin()
            .Node("hobbies").Within()
                .Node("hobby").InnerText("Fishing")
                .Node("hobby").InnerText("Photography")
                .Node("hobby").InnerText("Work");

Introduction: link

NuGet: link

    
21.02.2014 / 19:45