XmlSerializer throws InvalidOperationException when having a namespace prefix in the attribute - c#

I try to read an XML file that contains the following element:
<ho:CODED-TYPE ho:BASE-DATA-TYPE="A_UINT16" CATEGORY="STANDARD-LENGTH-TYPE" ENCODING="UNSIGNED">
My class to describe this node looks like that:
public ref class FIBEXCodedType
{
public:
[XmlAttribute("ho:BASE-DATA-TYPE")]
property String^ BaseDataType;
[XmlAttribute("CATEGORY")]
property String^ Category;
[XmlAttribute("ENCODING")]
property String^ Encoding;
FIBEXCodedType(void);
};
I get an InvalidOperationException from XmlSerializer.ctor telling me:
"Ungültiges Namenszeichen in 'ho:BASE-DATA-TYPE'." (this could be translated as "invalid character in: 'ho:BASE-DATA-TYPE'").
I also tried the following:
[XmlAttribute("BASE-DATA-TYPE", Namespace="http://www.asam.net/xml")]
property String^ BaseDataType;
But this didn't work either. This time without the error message but the unit test fails telling me, that the property is still set to "null".
I am completely stuck with this. So any help is appreciated
thanks in advance
EDIT: some more XML
<?xml version="1.0" ?>
<fx:FIBEX xmlns:fx="http://www.asam.net/xml/fbx" xmlns:ho="http://www.asam.net/xml" xmlns:can="http://www.asam.net/xml/fbx/can" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="fibex4can.xsd" VERSION="3.1.0">
<fx:CODING ID="codingSpeed">
<ho:SHORT-NAME>CodingSpeed</ho:SHORT-NAME>
<ho:DESC>Coding for speed values within this system.</ho:DESC>
<ho:CODED-TYPE ho:BASE-DATA-TYPE="A_UINT16" CATEGORY="STANDARD-LENGTH-TYPE" ENCODING="UNSIGNED">
<ho:BIT-LENGTH>16</ho:BIT-LENGTH>
</ho:CODED-TYPE>
</fx:CODING>

rewritten entire answer after edit by OP
My original understanding of the error was wrong. The error is thrown on the initialization of the serializer, not when you read your XML. You cannot use a colon : in a name. If you specify a namespace, do not specify the prefix. Actually, you hardly ever specify the prefix (which is just a placeholder for the namespace).
After doing so, you already noticed that the value ends up null. The reason is that the serializer defaults to unqualified attributes. If you have qualified attributes, it assumes the attribute namespace is different than the element's namespace. This will work:
<!-- this works (if namespaces are indeed different -->
<ho:CODED-TYPE fx:BASE=DATA-TYPE="A_UINT16"...>
<!-- this works, unqualified name takes namespace of parent element -->
<ho:CODED-TYPE BASE=DATA-TYPE="A_UINT16"...>
<!-- this fails, because XmlSerializer does not expect qualified attributes -->
<ho:CODED-TYPE ho:BASE=DATA-TYPE="A_UINT16"...>
This seems an odd bug. Here's somewhat similar report on thisn at MSDN, which helped me to the solution. Just mark the attribute as qualified. The following works with your input XML (note XmlSchemaForm.Qualified):
[XmlRoot(ElementName = "FIBEX", Namespace = "http://www.asam.net/xml/fbx")]
public class FIBEX
{
[XmlElement("CODING", Namespace = "http://www.asam.net/xml/fbx")]
public FIBEXCoding Coding { get; set; }
}
public class FIBEXCoding
{
[XmlElement("SHORT-NAME", Namespace = "http://www.asam.net/xml")]
public string ShortName { get; set; }
[XmlElement("DESC", Namespace = "http://www.asam.net/xml")]
public string ShortDescription { get; set; }
[XmlElement("CODED-TYPE", Namespace = "http://www.asam.net/xml")]
public FIBEXCodedType Codedtype { get; set; }
}
public class FIBEXCodedType
{
[XmlAttribute("BASE-DATA-TYPE",
Namespace = "http://www.asam.net/xml",
Form=XmlSchemaForm.Qualified)]
public string BaseDataType { get; set; }
[XmlAttribute("CATEGORY")]
public string Category { get; set; }
[XmlAttribute("ENCODING")]
public string Encoding { get; set; }
[XmlElement("BIT-LENGTH", Namespace = "http://www.asam.net/xml")]
public int BitLength { get; set; }
}

Related

Serializing ArrayList outputs ArrayOfAnyType

I do have a problem with serializing a ArrayList. Most propably I use wrong XML Attributes for it since I when I changed them it would not even serialize it and got errors like 'The type may not be used in this context.'
I need to use a non generic ArrayList. On adding [XmlArray("LineDetails")] made this code to run but the output is not correct, it should give me the LineDetails structure. Any idea how to fix this?
This is a part of a whole xml like Document > Header > LineCollection > LineDeatails.
The problem is only with this details if I use a standard string field it is ok but the range of the colletion if changing with every document.
[XmlType(TypeName = "LineCollection")]
public class LineCollection
{
public String LineCount{ get; set; }
// [XmlElement(ElementName = "LineDetails")]
[XmlArray("LineDetails")]
public ArrayList LineDetails{ get; set; }
}
public class LineDetails: ArrayList
{
public String LineNum{ get; set; }
public String ItemId{ get; set; }
public String ItemName{ get; set; }
//... only strings
}
public class Utf8StringWriter : StringWriter
{
public override Encoding Encoding => new UTF8Encoding(false);
}
public string Serialize()
{
// var xmlserializer = new XmlSerializer(this.GetType());
var xmlserializer = new XmlSerializer(this.GetType(), new Type[] { typeof(LineDetails) });
var Utf8StringWriter = new Utf8StringWriter();
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
using (var writer = XmlWriter.Create(Utf8StringWriter))
{
xmlserializer.Serialize(writer, this, xns);
return Utf8StringWriter.ToString();
}
}
And the incorrect output of this...
<LineColletion>
<LineDetails>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="ArrayOfAnyType"/>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="ArrayOfAnyType"/>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="ArrayOfAnyType"/>
</LineDetails>
</LineColletion>
it should be like this
<LineColletion>
<LineDetails>
<LineNum>1</LineNum>
<ItemId>Item_2321</ItemId>
<ItemName>TheItemName</ItemName>
</LineDetails>
<LineDetails>
<LineNum>2</LineNum>
<ItemId>Item_232100000</ItemId>
<ItemName>TheItemName0</ItemName>
</LineDetails>
<LineDetails>
<LineNum>3</LineNum>
<ItemId>Item_23217777</ItemId>
<ItemName>TheItemName7</ItemName>
</LineDetails>
</LineColletion>
Now the wrong xml looks like this...
<LineDetails>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="LineDetails">
<LineNum>1</LineNum>
<ItemId>Item_2321</ItemId>
<ItemName>TheItemName</ItemName>
</anyType>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="LineDetails">
<LineNum>2</LineNum>
<ItemId>Item_2321</ItemId>
<ItemName>TheItemName</ItemName>
</anyType>
</LineDetails>
You may generate the required XML by modifying your data model as follows:
[XmlType(TypeName = "LineColletion")] // Fixed: TypeName. But do you want LineColletion (misspelled) or LineCollection (correctly spelled)? Your XML shows LineColletion but your code used LineCollection.
public class LineCollection
{
public String LineCount{ get; set; }
[XmlElement("LineDetails", typeof(LineDetails))] // Fixed -- specify type(s) of items in the ArrayList.
public ArrayList LineDetails{ get; set; }
}
public class LineDetails // Fixed: removed inheritance from ArrayList.
{
public String LineNum{ get; set; }
public String ItemId{ get; set; }
public String ItemName{ get; set; }
//... only strings
}
Notes:
Your model makes LineDetails inherit from ArrayList. XmlSerializer will never serialize collection properties, it will only serialize collection items. In order to serialize it correctly, I removed the inheritance since you don't seem to be using it anyway.
If you really need LineDetails to inherit from ArrayList, you will need to implement IXmlSerializable or replace it collection with a DTO.
Implementing IXmlSerializable is tedious and error-prone. I don't recommend it.
Your LineDetails collection is serialized without an outer wrapper element. To make the serializer do this, apply XmlElementAttribute to the property.
ArrayList is an untyped collection, so you must inform XmlSerializer of the possible types it might contain. You have two options:
Assigning a specific type to a specific element name by setting XmlElementAttribute.Type (or XmlArrayItemAttribute.Type), OR
Adding xsi:type attributes by informing the serializer of additional included types. You are doing this by passing them into the constructor, which is why you are seeing the p5:type="LineDetails" attribute.
Since you don't want the attributes, you need to set the element name by setting the type like so:
[XmlElement("LineDetails", typeof(LineDetails))]
The XML element corresponding to your LineCollection is named <LineColletion>. Note that the spelling is inconsistent. You will need to set [XmlType(TypeName = "LineColletion")] to the name you actually want.
Demo fiddle here.

How do I specify a child element is in the global name space?

I am receiving some XML that looks like this.
<?xml version="1.0"?>
<parent xmlns="urn:place-com:spec.2004">
<childA xmlns="">123</childA>
<childB xmlns="">456</childB>
</parent>
I'm deserializing it into a class with C#'s XmlSerializer. It works, except the blank child namespaces are giving me trouble. Their properties in the class are null.
I understand the blank namespace puts the element in the global namespace. Probably not what is intended, but what I am receiving none the less.
If I manually delete the xmlns="" attribute from the child element it works. If I fill out the attribute with xmlns="testNamespace" and add the following attribute to the property in the class, it works.
[XmlElement(Namespace="testNamespace")]
public string childA
{ ... }
However, leaving the XML as is, and adding the following attribute does not work.
[XmlElement(Namespace="")]
How can I specify that an element has a blank namespace for deserialization?
For the xml presented in question the following class works perfectly.
[XmlRoot("parent", Namespace = "urn:place-com:spec.2004", IsNullable = false)]
public class Parent
{
[XmlElement("childA", Namespace = "")]
public string ChildA { get; set; }
[XmlElement("childB", Namespace = "")]
public string ChildB { get; set; }
}

Expecting element 'CustomerLeads' from namespace 'http://www.w3.org/2001/XMLSchema-instance'

Im getting the following error when running my application: Additional information: Error in line 2 position 64. Expecting element 'CustomerLeads' from namespace 'http://www.w3.org/2001/XMLSchema-instance'.. Encountered 'Element' with name 'CustomerLeads', namespace ''.
I dont understand why I am getting this error message because as you can see from the XML 'CustomerLeads' is included within the XML. If I take out the namespace the file will not read the Elements. Including the namespace seems to work but cant seem to figure out why I am getting this error. And how I can add the namespace without getting this error?
<?xml version="1.0" encoding="UTF-8"?>
<CustomerLeads xsi:noNamespaceSchemaLocation="BasicCustomerLead.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CustomerLead>
<FirstName>Paul</FirstName>
<LastName>Smith</LastName>
<Email>psmith#example.com</Email>
</CustomerLead>-<CustomerLead>
<FirstName>Nicole</FirstName>
<LastName>Farhi</LastName>
<Email>nicole.farhi#example.com</Email>
</CustomerLead>-<CustomerLead>
<FirstName>Raf</FirstName>
<LastName>Simons</LastName>
<Email>rafs#example.org</Email>
</CustomerLead>
</CustomerLeads>
Code:
namespace Customer
{
[DataContract(Name = "CustomerLeads", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public class CustomerLeads
{
[DataMember()]
public string FirstName { get; set; }
[DataMember()]
public string LastName { get; set; }
[DataMember()]
public string EmailAddress { get; set; }
public CustomerLeads unSortedLeads(string xmFilelPath)
{
// doc.Load("C:/Users/Admin/Downloads/potentialcustomers.xml");
ICollection<CustomerLeads> deserializedPerson;
CustomerLeads lead;
FileStream fs = new FileStream(xmFilelPath, FileMode.Open);
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(CustomerLeads));
lead = (CustomerLeads)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
FirstName = lead.FirstName.ToString();
// foreach(CustomerLeads leads in deserializedPerson.)
return lead;
}
}
}
you might try <xsi:CustomerLeads> xsi being the xml namespace. Seems like the error is saying it doesn't know what namespace CustomerLeads belongs to.
<xsi:CustomerLeads xsi:noNamespaceSchemaLocation="BasicCustomerLead.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CustomerLead>
<FirstName>Paul</FirstName>
<LastName>Smith</LastName>
<Email>psmith#example.com</Email>
</CustomerLead>-<CustomerLead>
<FirstName>Nicole</FirstName>
<LastName>Farhi</LastName>
<Email>nicole.farhi#example.com</Email>
</CustomerLead>-<CustomerLead>
<FirstName>Raf</FirstName>
<LastName>Simons</LastName>
<Email>rafs#example.org</Email>
</CustomerLead>
</xsi:CustomerLeads>
The prefix might be required for the other elements as well.
Your DataContract attribute asserts that the CustomerLeads element is expected to be in the http://www.w3.org/2001/XMLSchema-instance XML namespace. It is not. In your XML, CustomerLeads has no namespace (effectively an empty namespace).
Try removing the Namespace setting from your DataContract attribute.

Using a generic object to return data from a webservice method?

I have an ASMX webservice with a number of methods which will return XML.
The service returns various different objects and I have created a wrapper object which contains information about the request e.g:
[Serializable]
[XmlRoot("response")]
public class DtoWrapper<T>
{
[XmlElement("error")]
public bool Error { get; set; }
[XmlElement("error_message")]
public string ErrorMessage { get; set; }
[XmlElement("success")]
public bool Success { get; set; }
[XmlElement("friendly_message")]
public string FriendlyMessage { get; set; }
[XmlArray("result")]
[XmlArrayItem("item")]
public List<T> Payload { get; set; }
}
Now this works fine until I defined my second method with a different type. Then I get this error when I try and load the ASMX test page
The top XML element 'response' from namespace 'http://tempuri.org/'
references distinct types
MyProject.Web.webservices.DtoWrapper1[MyProject.BusinessLogic.ClassA]
and
MyProject.Web.webservices.DtoWrapper1[MyProject.BusinessLogic.ClassB].
Use XML attributes to specify another
XML name or namespace for the element
or types.
I have tried marking my objects up with [XmlType(Namespace="com.temp.A")] and [XmlType(Namespace="com.temp.B")] but it doesn't seem to help.
Any ideas? Will I have to create a wrapper object for each type I want to use?
EDIT: I've realised it's not actually the type arguments that are the problem. It's the fact that the [XmlRoot] tag is specified on the class. The serializer is treating them as 2 types but they have the same root element in the same namespace.
You cannot do this. XML has no concept of generics, neither do XML Schema or SOAP. As far as XML Schema is concerned, if it has the same element name and same namespace, then it's the same thing.
You cannot have a generic web service, as the concepts do not exist.

How to define a property with same name on two different types in ROWLEX?

If I have those two classes that have two different properties but with the same name:
[RdfSerializable]
public class Type1
{
[RdfProperty(true), Name = "title"]
public string Title { get; set; }
}
[RdfSerializable]
public class Type2
{
[RdfProperty(true), Name = "title"]
public string Title { get; set; }
}
and try to serialize them to RDF and validate them with http://www.w3.org/RDF/Validator/ service. Everything is Okay and they are correct.
But after I try to generate OWL files from those classes with OntologyExtractor.exe tool I get that message:
"Ontology extraction failed. http://test.org/1.0#title is assigned to more than one type."
This is strange message as the upper classes are correct and there are some RDF specifications that has same situation with different classes that have same named properties.
I expect it is a bug in ROWLEX. Your case is a valid one, but I assume I did not prepare for it when I wrote OntologyExtractor. I will try to release a fix as soon as possible.
EDIT: ROWLEX2.1 is released, you can download it from http://rowlex.nc3a.nato.int. Version 2.1 (among others) supports now the shared property functionality. The exact code in the question would still result the same error! To overcome that, you should alter the decoration of your code as follows:
[RdfSerializable]
public class Type1
{
[RdfProperty(true, Name = "title", ExcludeFromOntology=true)]
public string Title { get; set; }
}
[RdfSerializable]
public class Type2
{
[RdfProperty(true, Name = "title",
DomainAsType = new Type[]{typeof(Type1), typeof(Type2)})]
public string Title { get; set; }
}
Using the OntologyExtractor.exe, this code will result a OWL property of with an anonymous domain class that is the UNION of Type1 and Type2.
While this is technically perfectly correct solution, setting domains on properties limit their possible future reuse. As a solution, you might want to substitute the property domain with local restrictions. You can achieve that as follows:
[RdfSerializable]
public class Type2
{
[RdfProperty(true, Name = "title",
DomainAsType = new Type[]{typeof(Type1), typeof(Type2)},
UseLocalRestrictionInsteadOfDomain = true)]
public string Title { get; set; }
}
Should you leave UseLocalRestrictionInsteadOfDomain not set, ROWLEX chooses between domain and local restriction according to the current context.

Categories

Resources