Serialise empty strings as nulls so they are not output - c#

I've got an XSD which has been supplied by a third party and I've then converted this into classes using xsd.exe.
Due to the fact we do not own this XSD, I'm trying to not alter the generated classes in any way.
Without manually applying a blank string check to every single field we are mapping, e.g.:
!String.IsNullOrWhiteSpace(field) ? field : null;
I can't think of an easy way to serialise the blank strings in a way which means they are not output at all (no tag is produced).
The reason we cannot output blank strings is because the XSD has pattern constraints such as StringM50m1 which I believe means a minimum length of 1 is required (however, they are happy to not receive the tag if there is no content) which fail post-output validation with the following message:
The 'field' element is invalid - The value '' is invalid according to its datatype 'StringM50m1' - The Pattern constraint failed.
Any thoughts would be appreciated.

If you use XmlSerializer you can add XmlDefaultValue for required properties:
private static void Serialize()
{
XsdClass xc = new XsdClass();
xc.SomeString1 = string.Empty;
xc.SomeString2 = "value";
xc.SomeString3 = null;
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlDefaultValue = string.Empty;
foreach (var prop in TypeDescriptor.GetProperties(typeof(XsdClass)).Cast<PropertyDescriptor>().Where(x => !x.IsReadOnly && x.PropertyType == typeof(string)))
overrides.Add(typeof(XsdClass), prop.Name, attribs);
XmlSerializer serializer = new XmlSerializer(typeof(XsdClass), overrides);
string result;
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, xc);
result = textWriter.ToString();
}
}
public class XsdClass
{
public string SomeString1 { get; set; }
public string SomeString2 { get; set; }
public string SomeString3 { get; set; }
}

Related

Remove Quotes From Json Property Key In C# [duplicate]

I'm trying to get Json.Net to serialise a property name without quote marks, and finding it difficult to locate documentation on Google. How can I do this?
It's in a very small part of a large Json render, so I'd prefer to either add a property attribute, or override the serialising method on the class.
Currently, the it renders like this:
"event_modal":
{
"href":"file.html",
"type":"full"
}
And I'm hoping to get it to render like: (href and type are without quotes)
"event_modal":
{
href:"file.html",
type:"full"
}
From the class:
public class ModalOptions
{
public object href { get; set; }
public object type { get; set; }
}
It's possible, but I advise against it as it would produce invalid JSON as Marcelo and Marc have pointed out in their comments.
Using the Json.NET library you can achieve this as follows:
[JsonObject(MemberSerialization.OptIn)]
public class ModalOptions
{
[JsonProperty]
public object href { get; set; }
[JsonProperty]
public object type { get; set; }
}
When serializing the object use the JsonSerializer type instead of the static JsonConvert type.
For example:
var options = new ModalOptions { href = "file.html", type = "full" };
var serializer = new JsonSerializer();
var stringWriter = new StringWriter();
using (var writer = new JsonTextWriter(stringWriter))
{
writer.QuoteName = false;
serializer.Serialize(writer, options);
}
var json = stringWriter.ToString();
This will produce:
{href:"file.html",type:"full"}
If you set the QuoteName property of the JsonTextWriter instance to false the object names will no longer be quoted.
You can also try a regex replace, with a substitution, which could handle any serialized object, and replace the quotes for you.
For Example:
var options = new ModalOptions { href = "file.html", type = "full" };
string jsonText = JsonConvert.SerializeObject(options);
string regexPattern = "\"([^\"]+)\":"; // the "propertyName": pattern
Console.WriteLine(Regex.Replace(jsonText, regexPattern, "$1:"));
This would produce:
{href:"file.html",type:"full"}
I built a working web example here.
Explanation of regex substitutions are here.

C# Xml Serializable enum type

I want to load a XML file with XML serialization. The type now should be an enum type.
So the XML looks like this:
<Ressource name="ressource_name" type= "Integer" >
...
</Ressource>
And I wanted to load it into a class like this:
[Serializable]
public enum Res_Type
{
[XmlEnum(Name = "Integer")]
Integer,
[XmlEnum(Name = "Decimal")]
Decimal,
[XmlEnum(Name = "Text")]
Text
}
public class Ressource
{
[XmlAttribute]
public string name { set; get; }
[XmlAttribute]
public Res_Type type { get; set; }
}
When I search for this topic I only find different ways of solving it, then I need it to. I need to have the XML like shown above, but I have no idea how to load the information in type as an enum.
Update:
To test the serialization and the deserialization I am using this code:
Ressource res = new Ressource();
res.name = "ressource_name";
res.type = Res_Type.Integer;
XmlSerializer serializer = new XmlSerializer(res.GetType());
using (StreamWriter writer = new StreamWriter(#"h:\test.xml"))
{
serializer.Serialize(writer, res);
}
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Ressource));
StringReader stringReader = new StringReader(#"h:\test.xml");
res = (Ressource)xmlSerializer.Deserialize(stringReader);
And I am getting the error: InvalidOperationException
Your problem is that you are using a StringReader rather than a StreamReader:
StringReader stringReader = new StringReader(#"h:\test.xml");
This means that your code is attempting to deserialize the contents of the string literal #"h:\test.xml" itself rather than the file to which it refers. This of course fails because the string h:\test.xml is not even well-formed XML.
Instead you should do:
var fileName = #"h:\test.xml";
// Write the file as before
using (var reader = new StreamReader(fileName))
{
res = (Ressource)xmlSerializer.Deserialize(reader);
}
Working .Net fiddle here.

How to serialize a custom object with a list of another custom object in a particular schema when this list of custom objects is a generic type

I need to create a lot of XML files. They are very similar and they need to have the following schema:
<Servidor>
<VersaoLayout>0001</VersaoLayout> <!--Will not change-->
<CodigoUJ>001001</CodigoUJ> <!--Will not change-->
<ItemServidor> <!--A list of <T> that will change-->
<CPFServidor>13579024681</CPFServidor>
<NomeServidor>Fulano Pereira Tal</NomeServidor>
</ItemServidor>
</Servidor>
Note that the tags <VersaoLayout> and <CodigoUJ> are the same for all the files i need to create, the <ItemServidor> is the content that will vary. So i thought: Why not create just one class and use generics to handle the differences for this case? And for now my classes are the following:
//Kind of a container class that will hold the content that will vary.
[XmlRoot("Servidor", ElementName="Servidor")]
public class LayoutArquivoXML<T> where T : ItemLayout
{
[XmlElement("VersaoLayout")]
public string VersaoLayout { get; set; }
[XmlElement("CodigoUJ")]
public string CodigoUJ { get; set; }
//[XmlArrayItem("ItemServidor")]
public List<T> ItensLayout { get; set; }
// Constructors omited for simplicity
}
//A "ContentClass". I will have a bunch of classes similar to this
[XmlRoot("ItemServidor", ElementName = "ItemServidor")]
public class ServidorLayout : ItemLayout
{
[XmlElement("CPFServidor")]
public string CPFServidor { get; set; }
[XmlElement("NomeServidor")]
public string Nome { get; set; }
}
I am instantiating my "container class" this way:
LayoutArquivoXML<ServidorLayout> layout = new Layout.LayoutArquivoXML<ServidorLayout>("versao01", "999", lstItensLayout.ToList());
And my Serialization method is the following:
public string Serializador<T>(T objeto)
{
string xml = string.Empty;
using (var sw = new ISO8859StringWriter())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); // to omit XML namespaces
var xs = new XmlSerializer(typeof(T));
xs.Serialize(sw, objeto, ns);
}
return xml;
}
These objects graph are generating a XML with the following schema:
<Servidor>
<VersaoLayout>versao01</VersaoLayout>
<CodigoUJ>999</CodigoUJ>
<ItensLayout>
<ServidorLayout>
<CPFServidor>4252813450</CPFServidor>
<NomeServidor>Antonio de Sousa Muniz</NomeServidor>
</ServidorLayout>
</ItensLayout>
</Serv>
I dont want the tag <ItensLayout> tag in the XML. What i need to do to generate the XML in the desired schema?
Also, the root tag <Servidor> will need to change according to the file i am generating, imagine that i need to create another file where the root tag is Another, let say:
LayoutArquivoXML<AnotherLayout> layout = new Layout.LayoutArquivoXML<AnotherLayout>("versao01", "999", anotherItensLayout.ToList());
There are two separate issues.
I don't want the tag <ItensLayout> tag in the XML. What i need to do to generate the XML in the desired schema?
As explained in the documentation for Serializing an Array as a Sequence of Elements, to serialize an array (list, enumerable etc.) as flat sequence of XML elements, you need to apply XmlElement attribute to the property/field.
Also, the root tag <Servidor> will need to change according to the file i am generating
This is controlled by the XmlRoot attribute.
Now, all this applies if you are using concrete classes. In order to apply them dynamically to your generic class, you can use the XmlOverrides class and the XmlSerializer constructors accepting such parameter.
To do that, first add an optional parameter to your original method:
public string Serializador<T>(T objeto, XmlAttributeOverrides overrides = null)
{
string xml = string.Empty;
using (var sw = new ISO8859StringWriter())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); // to omit XML namespaces
var xs = new XmlSerializer(typeof(T), overrides);
xs.Serialize(sw, objeto, ns);
xml = sw.ToString();
}
return xml;
}
Then create a specific method like this:
public string Serializador<T>(LayoutArquivoXML<T> objeto, string rootElementName, string contentElementName)
where T : ItemLayout
{
var xmlAttrOverrides = new XmlAttributeOverrides();
// Root element name override
var xmlRootAttrs = new XmlAttributes();
xmlRootAttrs.XmlRoot = new XmlRootAttribute(rootElementName);
xmlAttrOverrides.Add(typeof(LayoutArquivoXML<T>), xmlRootAttrs);
// Content element name override
var xmlContentAttrs = new XmlAttributes();
xmlContentAttrs.XmlElements.Add(new XmlElementAttribute(contentElementName));
xmlAttrOverrides.Add(typeof(LayoutArquivoXML<T>), "ItensLayout", xmlContentAttrs);
// Call the original method passing the overrides
return Serializador(objeto, xmlAttrOverrides);
}
Now you can achieve your goal by using something like this:
var source = new LayoutArquivoXML<ServidorLayout>
{
VersaoLayout = "0001",
CodigoUJ = "001001",
ItensLayout = new List<ServidorLayout>
{
new ServidorLayout
{
CPFServidor = "13579024681",
Nome = "Fulano Pereira Tal",
},
}
};
var xml = Serializador(source, "Servidor", "ItemServidor");
which produces:
<Servidor>
<VersaoLayout>0001</VersaoLayout>
<CodigoUJ>001001</CodigoUJ>
<ItemServidor>
<CPFServidor>13579024681</CPFServidor>
<NomeServidor>Fulano Pereira Tal</NomeServidor>
</ItemServidor>
</Servidor>

Using XmlAttributeOverrides on Nested Properties

I am trying to use XmlAttributeOverrides to control which class properties appear in the xml after the class has been serialized. It works on properties that are on the "root" class but not on nested properties. Here is a simple example to illustrate what I'm trying to accomplish.
My class hierarchy is as follows:
public class Main
{
public string Name { get; set; }
public Location Address { get; set; }
}
public class Location
{
public string StreetAddress { get; set; }
public Contact ContactInfo{ get; set; }
}
public class Contact
{
public string PhoneNumber { get; set; }
public string EmailAddr { get; set; }
}
When I serialize Main(), I get something like this:
<Main>
<Name></Name>
<Address>
<StreetAddress></StreetAddress>
<ContactInfo>
<PhoneNumber></PhoneNumber>
<EmailAddr></EmailAddr>
</ContactInfo>
</Address>
</Main>
What I am able to do is keep either Name or Address from appearing by using this:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("Address"));
overrides.Add(typeof(Main), "Address", attribs);
xs = new XmlSerializer(typeof(Main), overrides);
What I need to also be able to do is keep Main.Address.ContactInfo from being serialized SOMETIMES (if it's empty). I tried the following but they didn't work:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Contact), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Contact), overrides);
and...
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Main.Address.ContactInfo), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main.Address.ContactInfo), overrides);
I've actually tried a lot more, including using XPath statements to designate the attribute name to target but didn't want to fill this page up with failed attempts. Is what I'm asking even possible by this method?
For anyone else trying to do this with XmlAttributeOverrides, turns out that #user1437872 was very close to finding the answer. Here is the override code to ignore the nested element ContactInfo.
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo"));
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);
There are easier ways to achieve what you're looking for.
You said that what you are trying to achieve is to not serialize /Main/Address/ContactInfo if ContactInfo contains no data.
If you leave your code as is, it will serialize all of Main's properties, whether they are null or empty or not. The first step, is you need to add a XmlSerializerNamespaces property to all of your objects or each empty object will be serialized as <myElement xsi:nil="true" />. This can be accomplished easily, as follows:
public MyXmlElement
{
public MyXmlElement()
{
// Add your own default namespace to your type to prevet xsi:* and xsd:*
// attributes from being generated.
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:myDefaultNamespace") });
}
[XmlElement("MyNullableProperty", IsNullable=false)]
public string MyNullableProperty
{
get
{
return string.IsNullOrWhiteSpace(this._myNullableProperty) ?
null : this._myNullableProperty;
}
set { this._myNullableProperty = value; }
}
[XmlNamespacesDeclaration]
public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } }
private XmlSerializerNamespaces _namespaces;
}
The code above declares a Namespaces property that holds all the relevant namespaces for the XML object. You should provide a default namespace for all of your objects (modeled after the code above). This prevents the xsi:* and xsd:* attributes from being output for your objects when they are serialized. Also, specify that the element is not nullable by using the System.Xml.Serialization.XmlElementAttribute.
Furthermore, by checking for string.IsNullOrWhiteSpace(someVariable) and returning null, then the
property will not be serialized when the above has been done.
So, putting this all together for your Location class:
public class Location
{
// You should have a public default constructor on all types for (de)sereialization.
public Location()
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:myNamespace"); // Default namespace -- prevents xsi:nil="true" from being generated, as well as xsd:* attributes.
});
}
public string StreetAddress
{
// If you don't want <StreetAddress xsi:nil="true" /> to be generated, do this:
get { return string.IsNullOrEmpty(this._streetAddress) ? null : this._streetAddress; }
// Otherwise, if you don't care, just do
// get;
// Only need to implement setter if you don't want xsi:nil="true" to be generated.
set { this._streetAddress = value; }
// Otherwise, just
// set;
}
private string _streetAddress;
[XmlElement("ContactInfo", IsNullable=false)]
public Contact ContactInfo
{
// You must definitely do this to prevent the output of ContactInfo element
// when it's null (i.e. contains no data)
get
{
if (this._contactInfo != null && string.IsNullOrWhiteSpace(this._contactInfo.PhoneNumber) && string.IsNullOrWhiteSpace(this._contactInfo.EmailAddr))
return null;
return this._contactInfo;
}
set { this._contactInfo = value; }
}
private Contact _contactInfo;
[XmlNamespacesDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
With these changes to your Location class, the empty ContactInfo property should no longer be serialized to XML when none of the properties are null, empty, or whitespace, or if ContactInfo itself is null.
You should make similar changes to your other objects.
See my other stackoverflow answers for more on .NET XML serialization:
XmlSerializer: remove unnecessary xsi and xsd namespaces
Omitting all xsi and xsd namespaces when serializing an object in .NET?
Suppress xsi:nil but still show Empty Element when Serializing in .Net
There is no need to add a ContactInfo element to the attribs
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);

Json.Net - Serialize property name without quotes

I'm trying to get Json.Net to serialise a property name without quote marks, and finding it difficult to locate documentation on Google. How can I do this?
It's in a very small part of a large Json render, so I'd prefer to either add a property attribute, or override the serialising method on the class.
Currently, the it renders like this:
"event_modal":
{
"href":"file.html",
"type":"full"
}
And I'm hoping to get it to render like: (href and type are without quotes)
"event_modal":
{
href:"file.html",
type:"full"
}
From the class:
public class ModalOptions
{
public object href { get; set; }
public object type { get; set; }
}
It's possible, but I advise against it as it would produce invalid JSON as Marcelo and Marc have pointed out in their comments.
Using the Json.NET library you can achieve this as follows:
[JsonObject(MemberSerialization.OptIn)]
public class ModalOptions
{
[JsonProperty]
public object href { get; set; }
[JsonProperty]
public object type { get; set; }
}
When serializing the object use the JsonSerializer type instead of the static JsonConvert type.
For example:
var options = new ModalOptions { href = "file.html", type = "full" };
var serializer = new JsonSerializer();
var stringWriter = new StringWriter();
using (var writer = new JsonTextWriter(stringWriter))
{
writer.QuoteName = false;
serializer.Serialize(writer, options);
}
var json = stringWriter.ToString();
This will produce:
{href:"file.html",type:"full"}
If you set the QuoteName property of the JsonTextWriter instance to false the object names will no longer be quoted.
You can also try a regex replace, with a substitution, which could handle any serialized object, and replace the quotes for you.
For Example:
var options = new ModalOptions { href = "file.html", type = "full" };
string jsonText = JsonConvert.SerializeObject(options);
string regexPattern = "\"([^\"]+)\":"; // the "propertyName": pattern
Console.WriteLine(Regex.Replace(jsonText, regexPattern, "$1:"));
This would produce:
{href:"file.html",type:"full"}
I built a working web example here.
Explanation of regex substitutions are here.

Categories