I want that users will write xml as this one:
<Root>
<Param>
<Int>134</Int>
</Param>
</Root>
or like this one:
<Root>
<Param>
<String>134</String>
</Param>
</Root>
AND, I want that the auto-generated C# code of this xsd (I use xsd.exe) will not generate new class for Param, but will simply use object.
First try
Now, when I write the xsd, I've started with this markup:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Root">
<xs:complexType>
<xs:sequence>
<xs:choice>
<xs:element name="Int" type="xs:int"/>
<xs:element name="String" type="xs:string"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
which yield this good code
public partial class Root {
public object Item { get; set; }
}
BUT the xml will look like:
<Root>
<Int>134</Int>
</Root>
which is bad for my purposes.
Second try
So, I've tried to fix the xsd, and that what I have now:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Root">
<xs:complexType>
<xs:sequence>
<xs:element name="Param">
<xs:complexType>
<xs:choice>
<xs:element name="Int" type="xs:int"/>
<xs:element name="String" type="xs:string"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
NOW, the xml is like i want (with "Param" element), BUT now the code is ugly:
public partial class Root {
public RootParam Param { get; set; }
}
public partial class RootParam {
public object Item { get; set; }
}
it creats unnessecery class (RootParam) that has in it only object. I would like to get code as beofre.
So...
How can I do it? How to force element in the XML, without creating stupid fake unnessecery class?
Related
I am trying to deserialize xml into c# classes.
I followed some documentation and generated some c# classes using the commands xsd foo.xml & xsd foo.xsd /classes
one of the classess generated by commands. The only part that fails to bind is the getMLIstResult
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public partial class EnvelopeBodyGetMListResponse
{
private string[] getMListResultField;
/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable = false)]
public string[] GetMListResult
{
get
{
return this.getMListResultField;
}
set
{
this.getMListResultField = value;
}
}
}
the xml used to create the xsd
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetMListResponse xmlns="http://tempuri.org/">
<GetMListResult>
<string>string</string>
<string>string</string>
</GetListResult>
</GetListResponse>
</soap:Body>
</soap:Envelope>
xsd used to create the class
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Envelope" targetNamespace="http://schemas.xmlsoap.org/soap/envelope/" xmlns:mstns="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:app1="http://tempuri.org/">
<xs:import namespace="http://tempuri.org/" schemaLocation="Envelope_app1.xsd" />
<xs:element name="Envelope" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" msdata:Prefix="soap">
<xs:complexType>
<xs:sequence>
<xs:element name="Body" msdata:Prefix="soap">
<xs:complexType>
<xs:sequence>
<xs:element name="GetMListResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="GetMListResult">
<xs:complexType>
<xs:sequence>
<xs:element name="string" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
a few things to note:
I am not able to change the way the xml is returned from an api I am trying to work with.
I also made a few changes to the xsd after it was generated.
serialization/deserialization works for the most part, but lists/arrays fail to bind
I have a sample to read a xml schema set for a xml file which contains different namespaces. For this i can get different schema for each namespace as i explained below.
Sample File:
<?xml version="1.0" encoding="utf-8"?>
<data xmlns:d="http://sampleschema/dataservices" xmlns:m="http://sampleschema/dataservices/metadata">
<content>
<m:properties>
<d:CustomerID>ALFKI</d:CustomerID>
<d:CompanyName>Alfreds Futterkiste</d:CompanyName>
</m:properties>
</content>
</data>
Sample Code to get XML Schema Set:
strFileName = #"C:\Sample\Sample.xml";
XmlReader reader = XmlReader.Create(strFileName);
XmlSchemaInference schema = new XmlSchemaInference();
XmlSchemaSet schemaSet = schema.InferSchema(reader);
It gives three different types of schemas while using the above code. But my requirement will be i need a single schema for the entire xml file which contain any number of namespaces in it. I have checked with possibilities of codes in msdn and stack overflow. I can't find any proper solution for this.
The expected schema output will be like below.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="data">
<xs:complexType>
<xs:sequence>
<xs:element name="content">
<xs:complexType>
<xs:sequence>
<xs:element name="m:properties">
<xs:complexType>
<xs:sequence>
<xs:element name="d:CustomerID" type="xs:string"></xs:element>
<xs:element name="d:CompanyName" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="xmlns:d" type="xs:string"></xs:attribute>
<xs:attribute name="xmlns:m" type="xs:string"></xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Any one can help to achieve this requirement.
Thanks in advance.
Try something like this :
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:m="http://www.w3.org/2001/XMLSchema" xmlns:d="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="data">
<xs:complexType>
<xs:sequence>
<xs:element name="content">
<m:complexType>
<m:sequence>
<m:element name="properties">
<d:complexType>
<d:sequence>
<d:element name="CustomerID" type="xs:string"></d:element>
<d:element name="CompanyName" type="xs:string"></d:element>
</d:sequence>
</d:complexType>
</m:element>
</m:sequence>
</m:complexType>
</xs:element>
</xs:sequence>
<d:attribute name="xmlns_d" type="d:string"></d:attribute>
<m:attribute name="xmlns_m" type="d:string"></m:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Consider the following simple schema:
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="MyRoot">
<xs:complexType>
<xs:sequence>
<xs:group ref="MyChoice" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:group name="MyChoice">
<xs:choice>
<xs:element name="a" type="xs:string"/>
<xs:element name="b" type="xs:string"/>
<xs:element name="c" type="xs:string"/>
</xs:choice>
</xs:group>
</xs:schema>
When turning this into C# code with xsd.exe (VS2017), I get the following output:
public partial class MyRoot {
private string[] aField;
private string[] bField;
private string[] cField;
// ... omitted for brevity ...
}
This code has a major problem: in the XSD/XML all of the sub-elements (a, b and c) were ordered.
This order is now lost in the C# class, since there are three different arrays for each of the sub elements.
Is xsd.exe doing it wrong? Or am I using it wrong? Or is the .xsd file flawed?
This is an example of an XSD for one of the templates that will be stored in the database. The "to_email", "first_name" etc are all tokens and I need to dynamically create a dictionary or an object that can be returned to the client that will give a list of these tokens. The idea behind this is if there is any change in a template then we will just insert another value in the database and it should automatically be created dynamically for returning it to client when they will query for this template.
How do I go about creating/parsing this?
I don't want create a class object with individual element as that would mean that I will have to change it for every addition of the elements. So it has to be something generic.
We should be able to return the set as JSON or XML based on what the client asks for.
How do I go about doing this?
Any help would be appreciated.
Thank you in advance.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd"
elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema.xsd"
xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="claim">
<xs:complexType>
<xs:sequence>
<xs:element name="to_email" type="xs:string"/>
<xs:element name="first_name" type="xs:string"/>
<xs:element name="last_name" type="xs:string"/>
<xs:element name="url" type="xs:string"/>
<xs:element name="received_date" type="xs:date"/>
<xs:element name="contact_number" type="xs:string"/>
<xs:element name="employer_name" type="xs:string"/>
<xs:element name="er_label" type="xs:string"/>
<xs:element name="er_flag" type="xs:integer"/>
<xs:element name="benefit_id" type="xs:integer"/>
<xs:element name="employee_id" type="xs:integer"/>
<xs:element name="employer_id" type="xs:integer"/>
<xs:element name="form_id" type="xs:integer"/>
<xs:element name="er_url" type="xs:string"/>
<xs:element name="tax" type="xs:string"/>
<xs:element name="Repeater" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="form_id" type="xs:integer"/>
<xs:element name="amount" type="xs:integer"/>
<xs:element name="expense_name" type="xs:string"/>
<xs:element name="date_of_service" type="xs:string"/>
<xs:element name="status_of_claim" type="xs:string"/>
<xs:element name="status_reason"/>
<xs:element name="Order_by" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Adding further comments:
The response to the client should be something like each property of the object representing a field. For example a object.
{
‘field1’: <FieldDefinition>
‘field2’: <FieldDefinition>
‘field3’: <FieldDefinition>
.
.
.
}
The object would contains the following properties like Type for example if it is a string or int, Display name i.e. the internal name could be first_name but the display name would FirstName# etc.
Also the could be an array as well. So this makes it little more complex.
Am I making any sense?
I believe you can do the XML processing or you can find that in Google. Plenty of examples are there. I just added the code for logic you have mentioned for the properties. //Your XML processing
var fields = new List<FieldDefinition>
{
new FieldDefinition{ Type="string", DisplayName="FirstName"},
new FieldDefinition{ Type="int", DisplayName="EmployeeId"}
};
var infr = new List<Infrastructure>
{
new Infrastructure { def1=fields.FirstOrDefault()}// loop through to assign each item
};
foreach (var item in infr)
Console.WriteLine(item.def1.DisplayName + " -" + item.def1.Type);
Console.Read();
}
}
public class Infrastructure
{
public FieldDefinition def1 { get; set; }
}
public class FieldDefinition
{
public string Type { get; set; }
public string DisplayName { get; set; }
}
You generate classes for them using the xsd.exe util
Seen here:
How to generate .NET 4.0 classes from xsd?
xsd your.xsd /classes
You can parse your template using XPath query or any generic XML processing. For any template change, you can opt for CacheDependency for File. You just cache the whole xml and if any changes to the tag/file then you insert that portion to the database and reload the cache. You can try SQLDependency also to get similar work.
Another option, you can go for filesystemwatcher which will raise event for changing in the file content. Then you just need to insert the added portion in the database.
Is there any way to deserialize elements containing either simple text or subelement to a string with XmlSerializer?
Xml sample:
<Attribute>
<AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">thisistext</AttributeValue>
<AttributeValue>
<e:Authorities xmlns:e="urn:dummy">
<e:Authority>ORG_CHIEF</esia-encoder:Authority>
</e:Authorities>
</AttributeValue>
</Attribute>
C# property:
[XmlElement("AttributeValue", IsNullable = true)]
public string[] AttributeValue { get; set; }
Deserialization of the first AttributeValue succeed, but the next one fails. No wonder, beacause ReadElementString method expects simple or empty content. I'm looking for a way to tell to serializer "put content of this element to a string, whatever it contains".
Actually, the 2nd value you have in your XML is:
<e:Authorities xmlns:e="urn:dummy">
<e:Authority>ORG_CHIEF</esia-encoder:Authority>
</e:Authorities>
This is no valid string dataType which is expected because of:
public string[] AttributeValue {get; set;}
If you are able to define it in XSD, you can use XSD2Code or xsd.exe to create a class for the XSD to deserialize into.
What about this one?
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="Attribute">
<xs:complexType>
<xs:sequence>
<xs:element name="AttributeValue" maxOccurs="unbounded">
<xs:complexType>
<xs:choice>
<xs:element name="AuthoritiesString" type="xs:string"/>
<xs:element name="AuthoritiesElement">
<xs:complexType>
<xs:sequence>
<xs:element name="Authority"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>