.NET XmlSerializer to Element FormDefault=Unqualified XML? - c#

I use C# code more-or-less like this to serialize an object to XML:
XmlSerializer xs1 = new XmlSerializer(typeof(YourClassName));
StreamWriter sw1 = new StreamWriter(#"c:\DeserializeYourObject.xml");
xs1.Serialize(sw1, objYourObjectFromYourClassName);
sw1.Close();
I want it to serialize like this:
<ns0:Header xmlns:ns0="https://mynamespace/">
<SchemaVersion>1.09</SchemaVersion>
<DateTime>2009-12-15T00:00:01-08:00</DateTime>
but instead, it is doing this:
<Header xmlns="https://mynamespace/">
<SchemaVersion xmlns="">V109</SchemaVersion>
<DateTime xmlns="">2010-03-08T18:21:09.100125-08:00</DateTime>
The way it is serializing doesn't work with the XPath I had planned to use, and doesn't match my BizTalk schema. Originally I built the class using XSD.exe from a BizTalk 2006 schema, then I use it for an argument to a WCF web service.
This might be related to an option called element FormDefault = Qualified or Unqualified. In BizTalk, my I have the schema set to "Unqualfiied" which is what I want.
Is there any way for the serializer to output "unqualified" results?
Thanks,
Neal Walters
Update:
Sample attribute on DateTime:
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public System.DateTime DateTime
{
get
{
return this.dateTimeField;
}
set
{
this.dateTimeField = value;
}
}
BizTalk provides for what it calls promoted (or distinguished) fields, which use XPath to pull out values of individual elements. I checked the XPath of BizTalk in a tool called StylusStudio, and Biztalk'x xpath didn't work with the xmlns='' fields above.
The first thing my WCF web service does is to serialize the object to a string (using UTF16 encoding) and store it in an XML column in a SQL database. It is from there I am seeing the above xml sample with the xmlns="".
XPath:
/*[local-name()='Header' and namespace-uri()='https://mynamespace/']/*[local-name()='DateTime' and namespace-uri()='']

The XPATH you're using does not match the namespaces of your XML. Your Header element, for instance, in in the https://mynamespace/, but your XPATH is searching in the http://mynamespace/ namespace.

My question was a bit muddled, so this answer may or may not help someone.
This is a fairly complex scenario, and half of my issues came from trying to simplify it to make an easy post here.
I was actually adding a new element programmatically with a C# routine (see "NewElement" below). The C# code did not set its namespace to an empty string, therefore I believe it is inheriting the namespace of the "Header" element.
I freaked out a little because I was jumping to the conclusion that DateTime should not have the "xmlns=""' when in fact it should. Even though DateTime falls under Header, it does not nor should not inherit the Header's namespace.
In BizTalk, typically only complex types have their own namespace, and DateTime as well as NewElement are simple types.
<Header xmlns="https://mynamespace/">
<SchemaVersion xmlns="">V109</SchemaVersion>
<DateTime xmlns="">2010-03-08T18:21:09.100125-08:00</DateTime>
<NewElement>myvalue</NewElement>
So in effect, the two XML's I posted originally are identical as far as XPath goes. If I insert a new element, I need to make sure it follows the same pattern.
I had written the C# routine to add the element more than a year ago, and it worked fine then, so I wasn't suspect that it was causing this problem.

Related

How can I extract values as strings from an xml file based on the element/property name in a generated .Net class or the original XSD?

I have a large complex XSD set.
I have C# classes generated from those XSDs using xsd.exe. Naturally, though the majority of properties in the generated classes are strings, many are decimals, DateTimes, enums or bools, just as they should be.
Now, I have some UNVALIDATED data that is structured in the correct XML format, but may well NOT be able to pass XSD validation, let alone be put into an instance of the relevant .Net object. For example, at this stage, for all we know the value for the element that should be a DateTime could be "ABC" - not even parseable as a DateTime - let alone other string elements respecting maxLength or regex pattern restrictions. This data is ready to be passed in to a rules engine that we already have to make everything valid, including defaulting things appropriately depending on other data items, etc.
I know how to use the various types in System.Xml to read the string value of a given element by name. Clearly I could just hand craft code to get out all the elements that exist today by name - but if the XSD changes, the code would need to be reworked. I'd like to be able to either directly read the XSD or use reflection on the generated classes (including attributes like [System.Xml.Serialization.XmlTypeAttribute(TypeName=...] where necessary) to find exactly how to recursively query the XML down to the the raw, unverified string version of any given element to pass through to the ruleset, and then after the rules have made something valid of it, either put it back into the strongly typed object or back into a copy of the XML for serialization into the object.
(It has occurred to me that an alternative approach would be to somehow automatically generate a 'stringly typed' version of the object - where there are not DateTimes etc; nothing but strings - and serialize the xml into that. I have even madly thought of taking the xsd.exe generated .cs file and search/replacing all the enums and base types that aren't strings to strings, but there has to be a better way.)
In other words, is there an existing generic way to pull the XElement or attribute value from some XML that would correspond to a given item in a .Net class if it were serialized, without actually serializing it?
Sorry to self-answer, and sorry for the lack of actual code in my answer, but I don't yet have the permission of my employer to share the actual code on this. Working on it, I'll update here when there is movement.
I was able to implement something I called a Tolerant XML Reader. Unlike most XML deserializing, it starts by using reflection to look at the structure of the required .Net type, and then attempts to find the relevant XElements and interpret them. Any extra elements are ignored (because they are never looked for), any elements not found are defaulted, and any elements found are further interpreted.
The main method signature, in C#, is as follows:
public static T TolerantDeserializeIntoType<T>(
XDocument doc,
out List<string> messagesList,
out bool isFromSuppliedData,
XmlSchemaSet schemas = null,
bool tolerant = true)
A typical call to it might look like this:
List<string> messagesList;
bool defaultOnly;
SomeType result = TolerantDeserializeIntoType<SomeType>(someXDocument, out messagesList, out defaultOnly);
(you may use var; I just explicitly put the type there for clarity in this example).
This will take any XDocument (so the only criteria of the original was that it was well-formed), and make an instance of the specified type (SomeType, in this example) from it.
Note that even if nothing at all in the XML is recognized, it will still not fail. The new instance will simply have all properties / public fields nulled or defaulted, the MessageList would list all the defaulting done, and the boolean out paramater would be FALSE.
The recursive method that does all the work has a similar signature, except it takes an XElement instead of an XDocument, and it does not take a schemaSet. (The present implementation also has an explicit bool to indicate a recursive call defaulting to false. This is a slightly dirty way to allow it to gather all failure messages up to the end before throwing an error if tolerant is false; in a future version I will refactor that to only expose publicly a version without that, if I even want to make the XElement version public at all):
public static T TolerantDeserializeXElementIntoType<T>(
ref XElement element,
ref List<string> messagesList,
out bool isFromSuppliedValue,
bool tolerant = true,
bool recursiveCall = false)
How it works, detail
Starting with the main call, the one with with an XDocument and optional SchemaSet:
If a schema Set that will compile is supplied (actually, it also looks for xsi:noNamespaceSchemaLocation as well) the initial XDocument and schemaSet call runs a standard XDocument.Validate() across the supplied XDocument, but this only collects any issued validation error callbacks. It won't throw an exception, and is done for only two reasons:
it will give some useful messages for the MessageList, and
it will populate the SchemaInfo of all XElements to
possibly use later in the XElement version.
(note, however, that the
schema is entirely optional. It is actually only used to resolve
some ambiguous situations where it can be unclear from the C#
object if a given XElement is mandatory or not.)
From there, the recursive XElement version is called on the root node and the supplied C# type.
I've made the code look for the style of C# objects generated by xsd.exe, though most basic structured objects using Properties and Fields would probably work even without the CustomAttributes that xsd.exe supplies, if the Xml elements are named the same as the properties and fields of the object.
The method looks for:
Arrays
Simple value types, explicitly:
String
Enum
Bool
then anything
else by using the relevant TryParse() method, found by reflection.
(note that nulls/xsi:nill='true' values also have to be specially
handled)
objects, recursively.
It also looks for a boolean 'xxxSpecified' in the object for each field or property 'xxx' that it finds, and sets it appropriately. This is how xsd.exe indicates an element being omitted from the XML in cases where null won't suffice.
That's the outline. As I said, I may be able to put actual code somewhere like GitHub in due course. Hope this is helpful to someone.

XML serialization and encoded attributes

I'm trying to deserialize an XML with the following element with .NET XmlSerializer :
<SomeElement Foo_x003a_Bar="123"/>
In the target class, there's the following declaration:
class SomeElement
{
//...
[XmlAttribute("Foo_x003a_Bar")]
public string Foo_Bar;
}
The attribute is not being read from XML. In the UnknownAttribute event handler, I see that Foo_x003a_Bar is not recognized, and the list of valid attributes (Args.ExpectedAttributes) instead includes Foo_x005F_x003a_Bar.
What's the deal here, please? 0x5F is the code for the undescore character. Other attributes in the same element/class with names that contain _x0020_ deserialize properly. Why does _x003a_ get some kind of a special treatment?
EDIT: dirty hackery in the form of search/replacing in the XML string before it's parsed helps. But still.
EDIT2: the functions that implement this kind of encoding are XmlConvert:EncodeName, XmlConvert:EncodeLocalName. The latter handles colons, the former doesn't. Looks like they're calling EncodeName...
EDIT3: filed a bug report with Microsoft. Please navigate there and click "I can too" if you can, too :)
Looks like you are right about the treatment for _x<char-code>_ element names (described here), which uses XmlConvert.EncodeLocalName.
Given that you've instantiated your XmlSerializer using the only the type argument, the namespace is assumed to be empty.
What should work in your case is to simply use the decoded element names in your XmlAttributeAttribute settings:
class SomeElement
{
//...
[XmlAttribute("Foo:Bar")]
public string Foo_Bar;
}
One important factor here is casing, the XML node needs to have <Foo_x003A_Bar>, with a capital A.
The encoded node name needs to be consistent with XmlConvert.EncodeLocalName:
XmlConvert.EncodeLocalName("Foo:Bar")
// Foo_x003A_Bar

"Namespace prefix not defined" when it actually is defined

I'm having trouble deserializing XML with an "undefined" namespace prefix which really is defined.
We've published an internal web service in C# which serves a variety of clients. A new client's IDE insists on declaring xsi:type for every element in its XML output, and they can't turn off this "feature".
The XML message they produce goes like this, where "namespace" is the correct namespace.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<myOperation xsi:type="ns1:namespace" xmlns="namespace" xmlns:ns1="namespace">
<inputString xsi:type="xsd:string">ABCDEF</inputString>
<books xsi:type="ns1:booksType">
<bookID xsi:type="xsd:string">ABC123</bookID>
<bookID xsi:type="xsd:string">DEF456</bookID>
</books>
<!-- ... snip... -->
</myOperation>
</soapenv:Body>
<books> is basically an array of strings.
The service method accepts as XmlNode, but XmlSerializer throws a "prefix 'ns1' not defined" error. (It is defined in a parent node, but apparently that is not good enough.) I have a similar problem using wsdl.exe to generate classes and deserialize the input for me.
Using XmlNamespaceManager to specify prefixes doesn't seem right -- akin to magic numbers, and I can't predict which prefix a given consumer will declare anyway. Is there a way to handle this without stripping the attributes out (books.Attributes.RemoveAll)? That doesn't feel particularly elegant either.
I've found that books.OuterXML does not contain any information for 'ns1' unless I hack the element inbound to use that prefix (), so I can see why it complains, but I don't yet understand why 'ns1' isn't recognized from its previous definition above.
Many thanks for any help, or at least education, someone can provide.
Edits: it works fine if I change <books> to use the prefix, i.e. <ns1:books xsi:type="ns1:booksType">. This works whether I've defined xmlns or no. That may be consistent with this answer, but I still don't see how I would feasibly declare the prefix in the service code.
#Chris, certainly. Hope I can strike a balance between "stingy with closed source" and "usable for those who would help". Here "books" is the XmlNode received in the service method parameter. (Not to get off topic, but will also humbly take suggestions to improve it in general; I'm still a novice.)
XmlSerializer xmlSerializer = new XmlSerializer(typeof(booksType));
StringReader xmlDataReader = new StringReader(books.OuterXml);
books = (booksType)xmlSerializer.Deserialize(xmlDataReader);
The class is pretty much this:
[Serializable()]
[XmlRoot("books", Namespace = "namespace")]
[XmlTypeAttribute(TypeName = "booksType", Namespace = "namespace")]
public class booksType
{
[XmlElement(ElementName = "bookID")]
public string[] bookIDs { get; set; }
}
Your deserialization code could look something like this:
XmlSerializer sz = new XmlSerializer(typeof(booksType));
var reader = new XmlNodeReader(booksXmlNode);
var books = sz.Deserialize(reader);
[EDIT] This is better, because the namespace declarations are preserved with the XmlNode, whereas converting to an XML string via OuterXml appears to slice off the namespace declaration for the ns1 prefix, and the serializer then barfs on the type attribute value containing this prefix. I imagine this is a bug in the XML implementation but maybe an XML guru can confirm this.
This should get you past the error you are seeing, but whether it solves the problem completely I'm not sure.
[FURTHER EDIT] As noted in the comments below, there is a bug in the .NET XmlSerializer which is causing the deserialization to fail. Stepping through the deserialization code in the generated assembly, there is a point where the following condition is tested:
(object) ((System.Xml.XmlQualifiedName)xsiType).Namespace == (object)id2_namespace))
Although the Namespace property of the XmlQualifiedName has the same value ('namespace') as the string variable id2_namespace, the condition is evaluating to false because it is coded as an object identity test rather than a test for string value equivalence. Failing this condition leads directly to the exception reported by OP.
As far as I can see, this bug will always cause deserialization to fail whenever the XML for the object being deserialized uses one prefix on the object's root element name, and another prefix (defined as the same namespace) on that element's xsi:type attribute.

Parsing an XML file -options?

I'm developing a system to pick up XML attachments from emails, via Exchange Web Services, and enter them into a DB, via a custom DAL object that I've created.
I've manage to extract the XML attachment and have it ready as a stream... they question is how to parse this stream and populate a DAL object.
I can create an XMLTextReader and iterate through each element. I don't see any problems with this other than that I suspect there is a much slicker way. The reader seems to treat the opening tag, the content of the tag and the closing tag as different elements (using reader.NodeType). I expected myValue to be considered one element rather than three. Like I said, I can get round this problem, but I'm sure there must be a better way.
I came across the idea of using an XML Serializer (completely new to me) but a quick look suggested that these can't handle ArrayLists and List (I'm using List).
Again, I'm new to LINQ, but LINQ-to-XML has also been mentioned, but examples I've seen seem rather complex - though that my simply be my lack of familiarity.
Basically, I don't want a cludged system, but I don't want to use any complicated technique with a learning curve, just because it's 'cool'.
What is the simplest and most effective way of translating this XML/Stream in to my DAL objects?
XML Sample:
<?xml version="1.0" encoding="UTF-8"?>
<enquiry>
<enquiryno>100001</enquiryno>
<companyname>myco</companyname>
<typeofbusiness>dunno</typeofbusiness>
<companyregno>ABC123</companyregno>
<postcode>12345</postcode>
<contactemail>me#example.com</contactemail>
<firstname>My</firstname>
<lastname>Name</lastname>
<vehicles>
<vehicle>
<vehiclereg>54321</vehiclereg>
<vehicletype>Car</vehicletype>
<vehiclemake>Ford</vehiclemake>
<cabtype>n/a</cabtype>
<powerbhp>130</powerbhp>
<registrationdate>01/01/2003</registrationdate>
</vehicle>
</vehicles>
</enquiry>
Update 1:
I'm trying to deserialize, based on Graham's example. I think I've set up the DAL for serialization, including specifying [XmlElement("whatever")] for each property. And I've tried to deserialize using the following:
SalesEnquiry enquiry = null;
XmlSerializer serializer = new XmlSerializer(typeof(SalesEnquiry));
enquiry = (SalesEnquiry)serializer.Deserialize(stream);
However, I get an exception:'There is an error in XML document (2, 2)'. The innerexception states {"<enquiry xmlns=''> was not expected."}
Conclusion (updated):
My previous problem was the fact that the element in the XML file (Enquiry) != the name of the class (SalesEnquiry). Rather than an [XmlElement] attribute for the class, we need an [XmlRoot] attribute instead. For completeness, if you want a property in your class to be ignored during serialization, you use the [XmlIgnore] attribute.
I've successfully serialized my object, and have now successfully taken the incoming XML and de-serialized it into a SalesEnquiry object.
This approach is far easier than manually parsing the XML. OK, there has been a steep learning curve, but it was worth it.
Thanks!
If your XML uses a schema (i.e. you're always going to know what elements appear, and where they appear in the tree), you could use XmlSerializer to create your objects. You'd just need some attributes on your classes to tell the serializer what XML elements or attributes they correspond to. Then you just load up your XML, create a new XmlSerializer with the type of the .NET object you want to create, and call the Deserialize method.
For example, you have a class like this:
[Serializable]
public class Person
{
[XmlElement("PersonName")]
public string Name { get; set; }
[XmlElement("PersonAge")]
public int Age { get; set; }
[XmlArrayItem("Child")]
public List<string> Children { get; set; }
}
And input XML like this (saved in a file for this example):
<?xml version="1.0"?>
<Person>
<PersonName>Bob</PersonName>
<PersonAge>35</PersonAge>
<Children>
<Child>Chris</Child>
<Child>Alice</Child>
</Children>
</Person>
Then you create a Person instance like this:
Person person = null;
XmlSerializer serializer = new XmlSerializer(typeof(Person));
using (FileStream fs = new FileStream(GetFileName(), FileMode.Open))
{
person = (Person)serializer.Deserialize(fs);
}
Update:
Based on your last update, I would guess that either you need to specify an XmlRoot attribute on the class that's acting as your root element (i.e. SalesEnquiry), or the XmlSerializer might be a bit confused that you're referencing an empty namespace in your XML (xmlns='' doesn't seem right).
XmlSerializer does support arrays & lists... as long as the contained type is serializable.
I have found Xsd2Code very helpful for this kind of thing: http://xsd2code.codeplex.com/
Basically, all you need to do is write an xsd file (an XML schema file) and specify a few command line switches. Xsd2Code will automatically generate a C# class file that contains all the classes and properties plus everything needed to handle the serialization. It's not a perfect solution as it doesn't support all aspects of XSD, but if your XML files are relatively simple collections of elements and attributes, it should be a nice short-cut for you.
There's another similar project on Codeplex called Linq to XSD (http://linqtoxsd.codeplex.com/), which was designed to enforce the entire XSD specification, but last time I checked, it was no longer being supported and not really ready for prime time. Thought it was worth a mention, though.

Creating something similar to System.ServiceModel.Syndication using .NET 3.5

If I am dealing with several standard xml formats what would be the best practice way of encapsulating them in C# 3.5? I'd like to end up with something similar to the System.ServiceModel.Syndication namespace.
A class that encapsulates the ADF 1.0 XML standard would be one example. It has the main XML root node, 6 child elements, 4 of which are required IIRC, several required and optional elements and attributes further down the tree. I'd like the class to create, at a minimum, the XML for all the required pieces all the way up to a full XML representation. (Make sense).
With LINQ 4 XML and extension classes, etc, etc, there has to be some ideas on quickly generating a class structure for use. Yes? No? :)
Am not sure if I gave enough details to get one correct answer but am willing to entertain ideas right now.
TIA
XML serialization seems a good approach. You could even use xsd.exe to generate the classes automatically...
EDIT
Note that the class names generated by the tool are usually not very convenient, so you might want to rename them. You might also want to change arrays of T to List<T>, so that you can easily add items where needed.
Assuming the class for the root element is named ADF, you can load an ADF document as follows :
ADF adf = null;
XmlSerializer xs = new XmlSerializer(typeof(ADF));
using (XmlReader reader = XmlReader.Create(fileName))
{
adf = (ADF)xs.Deserialize(reader);
}
And to save it :
ADF adf = ...; // create or modify your document
...
XmlSerializer xs = new XmlSerializer(typeof(ADF));
using (XmlWriter writer = XmlWriter.Create(fileName))
{
xs.Serialize(writer, adf);
}
Why not just follow the pattern of the SyndicationFeed object in the Syndication namespace? Create a class that takes in a Uri to the xml document or just takes in the document fragment.
Then parse the document based on your standards (this parsing can be done using LinqToXml if you wanted to, though regEx might be faster if you are comfortable with them). Throw exceptions or track errors appropriately when the document doesn't pass the specification rules.
If the document passes the parse step then break the pieces of the document out into public getter properties of your object. Then return the fully hydrated object back to the consumer for use
Seems pretty straight forward to me. Is that what you are after or are you looking for something more than this?

Categories