Data Contract Serializer - How to Omit the Outer Element of a Collection

Data Contract Serializer - How to omit the outer element of a collection

The DataContract serializer does not allow this degree of control over the resulted XML, you will have to use instead the XmlSerializer in order to achieve this.

DataContract deserialize XML - List of elements and properties in the same element

Try this... (uses XmlSerializer rather than DataContractSerializer)

Usings...

using System;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.IO;

Classes...(created from your XML using http://xmltocsharp.azurewebsites.net/)

[XmlRoot(ElementName = "FlightSegment")]
public class FlightSegment
{
[XmlElement(ElementName = "DepDate")]
public string DepDate { get; set; }
[XmlElement(ElementName = "DepTime")]
public string DepTime { get; set; }
[XmlElement(ElementName = "ArrDate")]
public string ArrDate { get; set; }
[XmlElement(ElementName = "ArrTime")]
public string ArrTime { get; set; }
[XmlElement(ElementName = "DepDay")]
public string DepDay { get; set; }
[XmlElement(ElementName = "ArrDay")]
public string ArrDay { get; set; }
[XmlElement(ElementName = "DepAirport")]
public string DepAirport { get; set; }
[XmlElement(ElementName = "DepAirportName")]
public string DepAirportName { get; set; }
[XmlElement(ElementName = "DepCityName")]
public string DepCityName { get; set; }
[XmlElement(ElementName = "ArrAirport")]
public string ArrAirport { get; set; }
[XmlElement(ElementName = "ArrAirportName")]
public string ArrAirportName { get; set; }
[XmlElement(ElementName = "ArrCityName")]
public string ArrCityName { get; set; }
[XmlElement(ElementName = "DepCountry")]
public string DepCountry { get; set; }
[XmlElement(ElementName = "ArrCountry")]
public string ArrCountry { get; set; }
[XmlElement(ElementName = "Airline")]
public string Airline { get; set; }
[XmlElement(ElementName = "AirName")]
public string AirName { get; set; }
[XmlElement(ElementName = "FlightNo")]
public string FlightNo { get; set; }
[XmlElement(ElementName = "BookingClass")]
public string BookingClass { get; set; }
[XmlElement(ElementName = "AirCraftType")]
public string AirCraftType { get; set; }
[XmlElement(ElementName = "ETicket")]
public string ETicket { get; set; }
[XmlElement(ElementName = "NonStop")]
public string NonStop { get; set; }
[XmlElement(ElementName = "DepTer")]
public string DepTer { get; set; }
[XmlElement(ElementName = "ArrTer")]
public string ArrTer { get; set; }
[XmlElement(ElementName = "AdtFareBasis")]
public string AdtFareBasis { get; set; }
[XmlElement(ElementName = "ChdFareBasis")]
public string ChdFareBasis { get; set; }
[XmlElement(ElementName = "InfFareBasis")]
public string InfFareBasis { get; set; }
}

[XmlRoot(ElementName = "RecommendedSegment")]
public class RecommendedSegment
{
[XmlElement(ElementName = "Duration")]
public string Duration { get; set; }
[XmlElement(ElementName = "FareBasis")]
public string FareBasis { get; set; }
[XmlElement(ElementName = "FlightSegment")]
public List<FlightSegment> FlightSegment { get; set; }
[XmlElement(ElementName = "DepAirport")]
public string DepAirport { get; set; }
[XmlElement(ElementName = "DepCity")]
public string DepCity { get; set; }
[XmlElement(ElementName = "DepCountry")]
public string DepCountry { get; set; }
[XmlElement(ElementName = "DepZone")]
public string DepZone { get; set; }
[XmlElement(ElementName = "ArrAirport")]
public string ArrAirport { get; set; }
[XmlElement(ElementName = "ArrCity")]
public string ArrCity { get; set; }
[XmlElement(ElementName = "ArrCountry")]
public string ArrCountry { get; set; }
[XmlElement(ElementName = "ArrZone")]
public string ArrZone { get; set; }
}

[XmlRoot(ElementName = "Availability")]
public class Availability
{
[XmlElement(ElementName = "RecommendedSegment")]
public RecommendedSegment RecommendedSegment { get; set; }
}

Code....

        try
{
Availability deserializedXML = new Availability();
// Deserialize to object
XmlSerializer serializer = new XmlSerializer(typeof(Availability));
using (FileStream stream = File.OpenRead(@"xml.xml"))
{
deserializedXML = (Availability)serializer.Deserialize(stream);
} // Put a break-point here, then mouse-over deserializedXML
}
catch (Exception)
{

throw;
}

Save your XML to a file (xml.xml) in the same folder as your .EXE.... Hope that helps.

How to omit individual list members from XML serialization?

Serialization is about serializing an entire object, with the possible exception of certain of the properties of the object.

If you want individual elements of the list to not be serialized, then don't put them in the list. Possibly create a copy of the list, but with the undesirable elements removed.

How can I ignore a property when serializing using the DataContractSerializer?

You might be looking for IgnoreDataMemberAttribute.

How to change the output of an XML in WCF

The DataContractSerializer does not support what you're wanting to do, but you can switch to use the XmlSerializer and achieve the outcome you're looking for:

First, update your ServiceContract to also have the XmlSerializerFormat attribute:

[ServiceContract, XmlSerializerFormat]
public interface IService1
{
[OperationContract]
CardTemplateDefinition SomeMethod(CardTemplateDefinition composite);

}

Then remove the [DataContract] attributes from your model classes and change the [DataContract] attributes for [XmlElement] attributes:

public class CardTemplateDefinition
{
[XmlElement("riskInfoList")]
public RiskInfoList RiskInfoList { get; set; }
}

public class RiskInfoList
{
[XmlElement("riskCount")]
public string RiskCount { get; set; }

[XmlElement("riskInfo")]
public List<RiskInfo> RiskInfo { get; set; }
}

public class RiskInfo
{
[XmlElement("riskType")]
public string RiskType { get; set; }

[XmlElement("riskDescription")]
public string RiskDescription { get; set; }

[XmlElement("autoRisk")]
public AutoRiskEntity AutoRisk { get; set; }

[XmlElement("dwellingRisk")]
public DwellingRiskEntity DwellingRisk { get; set; }
}

public class DwellingRiskEntity
{
[XmlElement("location")]
public string Location { get; set; }
}

public class AutoRiskEntity
{
[XmlElement("vin")]
public string Vin { get; set; }

[XmlElement("make")]
public string Make { get; set; }

[XmlElement("model")]
public string Model { get; set; }

[XmlElement("licensePlate")]
public string LicensePlate { get; set; }

[XmlElement("year")]
public string Year { get; set; }
}

DataContractJsonSerializer: collection type cannot be serialized when assigned to an interface

The inconsistency arises from a difference in how JSON and XML represent collections. For XML, the data contract serializers convert a collection to a nested set of elements -- an outer collection wrapper, and an inner element for each item in the collection. For JSON, the serializers convert a collection to an array containing objects. This seems reasonable, but there's a difference between the two: the XML outer element can have its own XML attributes, but JSON arrays cannot have their own properties -- there's simply no place for them in the standard.

This becomes an issue in dealing with type hints. Type hints are properties added to the serialized data to indicate, in the event of serializing an interface or base class of a class hierarchy, what concrete class was actually serialized. They are required to enable deserialization of the object without data loss. In XML, they appear as an i:type attribute:

<PriceList xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question32569055.V1">
<ListItems i:type="ListItems"> <!-- Notice the type hint here. -->
<ListItem i:type="TPH_PriceListJsonItem"> <!-- Notice the type hint here also. -->
<DestCityName>Cancun</DestCityName>
<StayDuration>7</StayDuration>
<LowestPrice>1111</LowestPrice>
</ListItem>
</ListItems>
</PriceList>

As you can see from your own example, type hints can be added for both collection and non-collection classes.

In JSON objects, they appear as an added property named "__type":

{
"__type": "TPH_PriceListJsonItem:#Question32569055.V3",
"DestCityName": "Cancun",
"StayDuration": 7,
"LowestPrice": 1111
}

But, as mentioned before, JSON arrays cannot have properties. So, what does DataContractJsonSerializer do for polymorphic collection types? Well, other than for a few standard collection interfaces which, as Fabian notes, are mapped to collection classes using hardcoded logic, it throws a cryptic exception to indicate that subsequent deserialization would be impossible. (For comparison, Json.NET introduces an extra container object to hold collection type information. See TypeNameHandling setting.)

The solution to this inconsistency is to serialize the collection explicitly as a concrete collection (TPH_PriceListJsonItems in your case) rather than as an interface:

[DataContract(Name = "PriceList")]
[KnownType(typeof(TPH_PriceListJsonItems))]
public class TPH_PriceListJson : TPH_IPriceList
{
[IgnoreDataMember]
public TPH_IPriceListItems ListItems
{
get
{
return ListItemList;
}
set
{
var list = value as TPH_PriceListJsonItems;
if (list == null)
{
list = new TPH_PriceListJsonItems();
if (value != null)
list.AddRange(value);
}
ListItemList = list;
}
}

[DataMember(Name = "ListItems")]
TPH_PriceListJsonItems ListItemList { get; set; }

public TPH_PriceListJson()
{
ListItemList = new TPH_PriceListJsonItems();
}
}

This eliminates the need for the type hint on the collection element while retaining it for collection members. It generates the following XML:

<PriceList xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question32569055.V3">
<ListItems> <!-- No type hint here any more. -->
<ListItem i:type="TPH_PriceListJsonItem"> <!-- But the type hint is still here. -->
<DestCityName>Cancun</DestCityName>
<StayDuration>7</StayDuration>
<LowestPrice>1111</LowestPrice>
</ListItem>
</ListItems>
</PriceList>

And produces the following JSON:

{
"ListItems": [
{
"__type": "TPH_PriceListJsonItem:#Question32569055.V3",
"DestCityName": "Cancun",
"StayDuration": 7,
"LowestPrice": 1111
},
]
}

This design allows the class TPH_IPriceListItems to control exactly what type of collection is used internally rather than leaving it up to users of the class, and thus seems like a preferable design overall.

SOAP WCF: Prevent deserialization to private fields

Your problem is that you auto-generated a type marked with XmlSerializer-specific attributes but are using WCF which uses DataContractSerializer by default. When DataContractSerializer tries to serialize a type marked with [SerializableAttribute] but no data contract attributes, it infers a contract similar to how BinaryFormatter works, namely that public and private fields should be serialized, not properties. (For more, see Types Supported by the Data Contract Serializer.)

To work around this problem, you can:

  1. Configure WCF to use XmlSerializer by applying [XmlSerializerFormatAttribute] to your service. To do this, see How to change Wcf to use a different serializer? and Using the XmlSerializer Class, e.g.:

    [ServiceContract]
    [XmlSerializerFormat]
    public class MyApi
    {
    [OperationContract]
    public void CreateMeterConfig2(MeterConfig Payload)
    {
    //do nothing
    }
    }
  2. Automatically generate your classes from your XSD with data contract attributes rather than XmlSerializer attributes by using svcutil.exe rather than xsd.exe. To do this, see Generate DataContract from XSD and ServiceModel Metadata Utility Tool (Svcutil.exe).

    Note that DataContractSerializer has some limitations compared to XmlSerializer. For instance it doesn't allow for properties to be selectively serialized as XML attributes rather than elements. For more, see XML Serialisation - When To Use DataContractSerializer / Binary / XMLSerialiser or Data Contract Serializer - How to omit the outer element of a collection. If your XSD is not compatible with these limitations you will need to use XmlSerializer.



Related Topics



Leave a reply



Submit