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

Asked

Viewed 3,223 times

3

I am 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 don’t value one of them, it’s null

When generating xml it does not generate the element

I would like it to generate even being empty or null

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

    <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...

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;
        }
    }
  • Friend, look at my edition. I managed to solve using ShouldSerialize.

4 answers

1

Inform 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 described your problem right it should solve.

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

  • Then, it ends up getting <phone xsi:nil="true" The same works for classes? in Xmlroot? in case there was a composition of a class

  • Fernando, as the doc says, he ends up getting xsi:nil, and I would like it to be for ex: <phone></phone>

  • @Rod the support to serialize 'null' elements of this class natively that I know is this, but I don’t see much reason to serialize a 'null' attribute, most deserialization Apis is that if the node is not found, it is because it is 'null''.

0


You can create a class that inherits from Xml.Xmltextwriter and overwrite the Writeendelement method, I did this in an NF-e component, where the whole 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

When serializing use this class:

serializer.Serialize(new FullEndingXmlTextWritter(), obj);
  • Mine is for eSocial, rsrsrs, unfortunately I think it hasn’t worked yet anyway...

  • It works yes, at the time of serializing the Writeendelement method should be called, puts a breakpoint to see if it is being called.

  • Only if I’m doing something wrong, because here it doesn’t work, rsrsrs

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 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 I load the xml value, the value null possibly exchanged for an empty string.

EDIT 2 It does not work, but it is possible to make it work with gambiarra down below. =\

Take your class pessoa stay 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’s gonna work, but that’s not a very nice thing to do... after all it’s a scam.

EDIT 3 There’s no way to do it any other way than just using the 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, that we can do a post-processing in the generated xml text... we could for example use a regex, but this is not very recommendable, especially 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’d just have to add IsNullable = true in the attributes XmlElement of its properties, and soon after generating xml use regex substitution.

But then don’t complain if people call you names for using regex with xml... hehe!

  • 1

    That’s gambiarra, my friend.

  • So, there are N classes, various compositions, various properties, it’s very strange to set "" to all

  • This is the default xml behavior. There is no other way to do it using this serializer. If you use Isnullable, your xml will come out with the following: <cpf xsi:nil="true" /> instead of <cpf></cpf>. Unless, of course, this is acceptable.

  • I agree with @Miguelangelo, null is different from "" and the XML data standard treats this difference with this behavior. If the XML file shows <phone></phone> is because the phone attribute is "" and not null.

  • Vish @Miguelangelo, if I have 100 properties, do it for all? gets very busy, rsrsrsrs

  • I edited there, with what I think is the last alternative... I’ve searched the source code of Xmlserializer (specifically the file XmlSerializationWriter.cs) and I didn’t find any option to do that.

  • @Miguelangelo I made an Annotation that goes through the elements, and the ones that are null, arrow " ", rsrsrsrs until then, I will leave so...

  • @Rod Put your solution then... can be useful to other people with the same problem. =)

Show 3 more comments

0

You can use the library XmlOutput. 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 (in English): http://improve.dk/xmldocument-fluent-interface/

Nuget: http://www.nuget.org/packages/XmlOutput/

Browser other questions tagged

You are not signed in. Login or sign up in order to post.