Disable emmiting empty list elements in Xml Serializer in C# - c#

I would like to disable emmiting empty elements in Xml in List.
I know about PropertyNameSpecified pattern but I don't know how to apply it to the list.
I have list of elements and it is being serialized. Some of those elements are empty and are producing empty Xml elements in that list (what I don't want).
My sample code:
public class ConditionsCollectionModel
{
[XmlElement("forbidden")]
public List<ForbiddenModel> ForbiddenCollection { get; set; }
[XmlElement("required")]
public List<RequiredModel> RequiredCollection { get; set; }
}
public class ForbiddenModel : IXmlSerializable
{
public string Value { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
Value = reader.ReadElementString("forbidden");
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(Value);
}
}
public class RuleModel
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("conditions")]
public ConditionsCollectionModel Conditions { get; set; }
}
It produces Xml in form like:
<rule>
<name>SR</name>
<conditions>
<forbidden />
<forbidden />
<forbidden>Ftest</forbidden>
<required>test</required>
<required>test2</required>
</conditions>
</rule>
I don't want those empty elements in conditions list.

The question is rather old but I experienced the same problem today. So for anybody reading this topic, this is the solution that works for me.
Add "ShouldSerialize{PropertyName}"
public class ConditionsCollectionModel
{
[XmlElement("forbidden")]
public List<ForbiddenModel> ForbiddenCollection { get; set; }
[XmlElement("required")]
public List<RequiredModel> RequiredCollection { get; set; }
public bool ShouldSerializeForbiddenCollection(){
return (ForbiddenCollection !=null && ForbiddenCollection.Count>0);
}
see: MSDN

Since ForbiddenCollection is list of Forbidden Class, you can set value of an empty ForbiddenClass object to null while inserting into the List.

Related

C# XML Serialization exclude writing class name

The layout I'm going for is like the below layout (this is part of a bigger structure):
<Products>
<R001 Retail=\"2.289\" Rank=\"1\" Code=\"001\" />
<R002 Retail=\"2.289\" Rank=\"2\" Code=\"002\" />
<R003 Retail=\"2.889\" Rank=\"3\" Code=\"003\" />
<R004 Retail=\"0\" Rank=\"4\" Code=\"0\" />
<R011 Retail=\"0\" Rank=\"7\" Code=\"0\" />
</Products>
So I have a class that has a property called Products. That property is a list of a class I call ProductExport. ProductExport has 3 properties that I mark with the XmlAttribute attribute and they are called Retail, Rank, Code. In ProductExport I implement IXmlSerializable so I can implement WriteXml() in which I make the tag names those R00X tags. That all works fine, however since ProductExport is it's own class the XmlSerializer writes a tag for each ProductExport in the list. I don't want a ProductExport tag. I would have thought by implementing IXmlSerializable on ProductExport I would control everything about how a ProductExport gets written, including NOT writing it's class name, but it seems to not be the case. How do I restrict writing it's class name?
public class StoresExport
{
public int ID { get; set; }
public int Name { get; set; }
public string State { get; set; }
public int DistrictID { get; set; }
public int? RegionID { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
public List<CompetitorLocation> RelatedLocations { get; set; }
public List<ProductExport> Products { get; set; }
}
public class ProductExport : IXmlSerializable
{
public float Retail { get; set; }
public int Rank { get; set; }
public string Code { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
// we aren't ever reading just writing
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("R" + Code);
writer.WriteAttributeString("Retail", Retail.ToString());
writer.WriteAttributeString("Rank", Rank.ToString());
writer.WriteAttributeString("Code", Code);
writer.WriteEndElement();
}
}
With that code this is the output which I don't want:
<Products>
<ProductExport>
<R001 Retail=\"0\" Rank=\"1\" Code=\"001\" />
</ProductExport>
<ProductExport>
<R002 Retail=\"0\" Rank=\"2\" Code=\"002\" />
</ProductExport>
<ProductExport>
<R003 Retail=\"0\" Rank=\"3\" Code=\"003\" />
</ProductExport>
<ProductExport>
<R004 Retail=\"0\" Rank=\"4\" Code=\"004\" />
</ProductExport>
<ProductExport>
<R011 Retail=\"0\" Rank=\"7\" Code=\"011\" />
</ProductExport>
</Products>
Per the docs, it's the parent element that will write the start and end elements for ProductExport:
The WriteXml implementation you provide should write out the XML representation of the object. The framework writes a wrapper element and positions the XML writer after its start. Your implementation may write its contents, including child elements. The framework then closes the wrapper element.
So to do this, you need to write the element name you want within Products. The simplest way to do this is to replace the List<ProductExport> with your own class that also implements IXmlSerializable.
When this is serialized, the framework will have already written the Product element (and will close it after), so, sticking with the contract, all you have to write is the start and end elements for each of your items and delegate the writing of their content to their implementation of IXmlSerializable:
public class Products : IXmlSerializable, IEnumerable<ProductExport>
{
private readonly List<ProductExport> _products = new List<ProductExport>();
public void Add(ProductExport product) => _products.Add(product);
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader)
{
throw new NotSupportedException();
}
public void WriteXml(XmlWriter writer)
{
foreach (var product in this)
{
writer.WriteStartElement("R" + product.Code);
product.WriteXml(writer);
writer.WriteEndElement();
}
}
public IEnumerator<ProductExport> GetEnumerator() => _products.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
See this fiddle for a working demo.

Omitting elementwrappers and getting the xml root value

<root xmlns:test="url" test:attr1="10" test:attr2="someValue>
<elementwrapper>
<secondElementwrapper>
<element>someValue</element>
<differentelement>anotherValue</differentelement>
</secondElementwrapper>
</elementwrapper>
</root>
The following classes are made:
public class XmlEntities
{
[XmlRoot("root")]
public class Root
{
[XmlElement("elementwrapper")]
public Elementwrapper Elementwrapper{ get; set; }
}
public class Elementwrapper
{
[XmlElement("secondElementwrapper")]
public SecondElementwrapper SecondElementwrapper{ get; set; }
}
public class Values
{
[XmlElement("element")]
public string Element{ get; set; }
[XmlElement("differentelement")]
public string Differentelement{ get; set; }
}
}
And here is where i serialize and deserialize the xml:
var reader = XmlReader.Create(url);
var xmlRecord = new XmlEntities.Root();
try
{
var serializer = new XmlSerializer(typeof(XmlEntities.Root));
xmlRecord = (XmlEntities.Root)serializer.Deserialize(reader);
reader.Close();
}
catch (Exception e) { }
I want to access xmlRecord.Element instead of xmlRecord.Elementwrapper.Values.Element
How do i get the test:attr1 value?*
If i remove the class Elementwrapper and Root, xmlRecord returns null.
[XmlAttribute] inside the Root class to get attr1 value doesnt work for me.
Thank you!
EDIT:
The element was a copy/paste wrong doing. Fixed that. I also added :test infront of the attr1, forgot that.
EDIT:
Adding the following as sgk mentioned, inside the root class, allowed me to access the attribute
[XmlAttribute("attr1", Namespace = "url")]
public string attr { get; set; }
EDIT: And is there a way to map the classes differently? So i can access xmlRecord.Element directly?
EDIT: #TonyStark seems like what i want needs to be approached a different way, but then again this already works i just need to access the element trough the nodes (xmlRecord.elementwrapper.secondelementwrapper.element) for those that are wondering.
To access the attribute of root i simply use : xmlRecord.attr after i added the xmlattribute that its written above.
I want to access xmlRecord.Element instead of xmlRecord.Elementwrapper.Values.Element - Based on the XML structure you have, to Deserialize and access the <element> value and <differentelement> value - you need to go through root-->elementwrapper-->secondElementwrapper.
How do i get the attr1 value? - add [XmlAttribute("attr1")] inside class Root
Then, [XmlElement("<elementwrapper>")] should be [XmlElement("elementwrapper")] otherwise when you Deserialize you will always get null as there is no matching element.
See below
public class XmlEntities
{
[XmlRoot("root")]
public class Root
{
[XmlElement("elementwrapper")]
public Elementwrapper Elementwrapper { get; set; }
[XmlAttribute("attr1", Namespace="url")]
public string attr1;
}
public class Elementwrapper
{
[XmlElement("secondElementwrapper")]
public SecondElementwrapper SecondElementwrapper { get; set; }
}
public class SecondElementwrapper
{
[XmlElement("element")]
public string Element { get; set; }
[XmlElement("differentelement")]
public string Differentelement { get; set; }
}
}

C# deserializing nested elements with only one item

I use C# to deserialize a XML file. My XML file has the format:
<Produced_by >
<Producing_Unit>
<Unit ID="" Name=""/>
</Producing_Unit>
</Produced_by>
When deserializing I want to remove the middleman Producing_Unit. Since Produced_by will always contain only one subelement Producing_Unit specifying the Unit.
My initial thoughts on how to implement doesn't work:
public class Unit
{
public string Name { get; set; }
public string ID { get; set; }
}
public class Produced_by
{
[XmlElement("Producing_Unit")]
[XmlElement("Unit")]
public Unit Unit { get; set; }
}
It could be soleved by using [XmlArray("Producing_Unit"), XmlArrayItem("Unit")]
and then having Produced_by contain: public List<Unit> {get;set;}. But that's not what I want.
As far as I know, it's not possible to use the XML with the "Producing_Unit" tag but ditch the matching Producing_Unit class with the standard attributes short of implementing the IXmlSerializable interface. Your best bet would be to separate your application/business logic from your serialization layer.
Keep your serialization data model simple and matching your XML schema (this means including the wrapping Producing_Unit class), then simply convert to/from that data model and a cleaner data model (without Producing_Unit) for the rest of your application to work with.
EDIT: Here's an implementation using the IXmlSerializable interface. I just whipped it off and honestly, don't know if it will work for all cases.
public class Unit
{
public string Name { get; set; }
public string ID { get; set; }
}
public class Produced_by : IXmlSerializable
{
public Unit Unit { get; set; }
public void WriteXml (XmlWriter writer)
{
writer.WriteStartElement("Produced_by");
writer.WriteStartElement("Producing_Unit");
writer.WriteStartElement("Unit");
writer.WriteAttributeString("ID", this.Unit.ID);
writer.WriteAttributeString("Name", this.Unit.Name);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
}
public void ReadXml (XmlReader reader)
{
while (reader.Read())
{
if (reader.Name == "Unit")
{
this.Unit = new Unit()
{
Name = reader.GetAttribute("Name"),
ID = reader.GetAttribute("ID")
};
break;
}
}
}
public XmlSchema GetSchema()
{
return(null);
}
}
I suspect that I'm performing the reading in a poor fashion, but this works in my local test. I still recommend separating your application and serialization concerns though and avoiding having to write an implementation like this.
You could try this:
public class Unit
{
public string Name { get; set; }
public string ID { get; set; }
}
public class Producing_Unit
{
public Unit Unit { get; set; }
}
public class Produced_by
{
private Producing_Unit producing_unit;
public Producing_Unit Producing_Unit //This can't be auto-implemented since can write to properties of properties.
{
get { return producing_Unit; }
set { producing_Unit = value; }
}
[XmlIgnoreAttribute]
public Unit Unit
{
get { return producing_Unit.Unit; }
set { producing_Unit.Unit = value; }
}
}
Yes, it doesn't get rid of the 'middle-men,' but you can ignore them.

XML Deserialization returns empty array

yet another XML Deserialization question.
I have checked several other threads and tried most of the solutions there, but to no avail.
The XML I receive can't be modded (or at least not easily) here it is:
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<ActueleVertrekTijden>
<VertrekkendeTrein>
<RitNummer>37047</RitNummer>
<VertrekTijd>2012-11-13T15:40:00+0100</VertrekTijd>
<EindBestemming>Sneek</EindBestemming>
<TreinSoort>Stoptrein</TreinSoort>
<Vervoerder>Arriva</Vervoerder>
<VertrekSpoor wijziging=\"false\">3</VertrekSpoor>
</VertrekkendeTrein>
<VertrekkendeTrein>
<RitNummer>10558</RitNummer>
<VertrekTijd>2012-11-13T15:46:00+0100</VertrekTijd>
<EindBestemming>Rotterdam Centraal</EindBestemming>
<TreinSoort>Intercity</TreinSoort>
<RouteTekst>Heerenveen, Steenwijk, Utrecht C</RouteTekst>
<Vervoerder>NS</Vervoerder>
<VertrekSpoor wijziging=\"false\">4</VertrekSpoor>
</VertrekkendeTrein>
<VertrekkendeTrein>
<RitNummer>37349</RitNummer>
<VertrekTijd>2012-11-13T15:59:00+0100</VertrekTijd>
<EindBestemming>Groningen</EindBestemming>
<TreinSoort>Sneltrein</TreinSoort>
<RouteTekst>Buitenpost</RouteTekst>
<Vervoerder>Arriva</Vervoerder>
<VertrekSpoor wijziging=\"false\">5b</VertrekSpoor>
</VertrekkendeTrein>
</ActueleVertrekTijden>
There are more elements (always a minumum of 10)
Now these are the classes I am deserializing too:
[Serializable, XmlRoot(ElementName="ActueleVertrekTijden", DataType="VertrekkendeTrein", IsNullable=false)]
public class ActueleVertrekTijden
{
[XmlArray("ActueleVertrekTijden")]
public VertrekkendeTrein[] VertrekLijst { get; set; }
}
[Serializable]
public class VertrekkendeTrein
{
[XmlElement("RitNummer")]
public string RitNummer { get; set; }
[XmlElement("VertrekTijd")]
public string VertrekTijd { get; set; }
[XmlElement("EindBestemming")]
public string EindBestemming { get; set; }
[XmlElement("Vervoerder")]
public string Vervoerder { get; set; }
[XmlElement("VertrekSpoor")]
public string VertrekSpoor { get; set; }
}
I omitted the others for the time being. The XmlRoot part I added because I got a "xmlsn="-error. So had to set the XmlRoot.
Now the Deserializer:
public ActueleVertrekTijden Deserialize<ActueleVertrekTijden>(string s)
{
var ser = new XmlSerializer(typeof(ActueleVertrekTijden));
ActueleVertrekTijden list = (ActueleVertrekTijden)ser.Deserialize(new StringReader(s));
return list;
}
It does return a ActueleVertrekTijden class but the VertrekLijst array remains null
You need to omit the wrapper namespace, because your array elements are appearing directly below the container ActueleVertrekTijden class, without any collection wrapper element. i.e. change
[XmlArray("ActueleVertrekTijden")]
public VertrekkendeTrein[] VertrekLijst { get; set; }
to
[XmlElement("VertrekkendeTrein")]
public VertrekkendeTrein[] VertrekLijst { get; set; }
Reference here

CollectionDataContract serialization not adding custom properties (DataMember)

We have a legacy system that needs to be fed (XML) data in a most unstructured format. Is the following even possible with the .NET DataContractSerializer?
Given the following DataContracts
[CollectionDataContract(Name = "Options", ItemName = "Option")]
public class OptionItemCollection : List<OptionItem>
{
[DataMember(Name = "Name")]
public string Name { get; set; }
public OptionItemCollection()
{
}
public OptionItemCollection(IEnumerable<OptionItem> items) : base(items)
{
}
}
[DataContract(Name = "Option")]
public class OptionItem
{
[DataMember]
public string Text { get; set; }
[DataMember]
public string Value { get; set; }
}
Is it possible to serialize this collection directly into the following XML representation:
<Options>
<Name>Juices</Name>
<Option Value="1">Orange Juice</Option>
<Option Value="2">Pineapple</Option>
<Option Value="3">Fruit Punch</Option>
</Options>
NOTE:
This is exactly how the legacy system expects the data to be submitted.
Or Even:
<Options>
<Name>Juices</Name>
<Option><Value>1</Value><Text>Orange Juice</Text></Option>
<Option><Value>2</Value><Text>Pineapple</Text></Option>
<Option><Value>3</Value><Text>Fruit Punch</Text></Option>
</Options>
Also NOTE that the emphasis is on the Name and Option element residing within the Options element.
Yes. Although the DataContractSerializer doesn't explicitly support XML attributes, you can hand-roll it. Try this:
[CollectionDataContract(Name = "Options", ItemName = "Option")]
public class OptionItemCollection : List<OptionItem>
{
[DataMember(Name = "Name")]
public string Name { get; set; }
public OptionItemCollection()
{
}
public OptionItemCollection(IEnumerable<OptionItem> items)
: base(items)
{
}
}
// note, remove attributes
public class OptionItem : IXmlSerializable
{
public string Text { get; set; }
public string Value { get; set; }
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Value", Value);
writer.WriteElementString("Text", Text);
}
public void ReadXml(XmlReader reader)
{
// implement if necessary
throw new NotImplementedException();
}
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new NotImplementedException();
}
}
No, this is not possible with the DataContractSerializer (DCS). The DCS doesn't allow unwrapped collection elements. So you cannot have this:
<a>
<b/>
<b/>
<b/>
<c/>
</a>
But you can have this:
<a>
<bb>
<b/>
<b/>
<b/>
</bb>
<c/>
</a>
In your scenario you'll need to use the XmlSerializer.

Categories