Help with implementing IXmlSerializable on this XML - asp.net

Here's the XML:
<?xml version="1.0" encoding="utf-8" ?>
<SAPPHIRE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TRANSACTION-CODE>NEW</TRANSACTION-CODE>
<CUSTOMER-NUMBER>100398598</CUSTOMER-NUMBER>
<CUSTOMER-NAME>CART DUDE</CUSTOMER-NAME>
<ACCOUNT-TYPE />
<PERSON FNAME="CART" LNAME="DUDE" RESPONSIBLITY="CART DUDE" />
<SOURCE>cplestore</SOURCE>
<TRAN-REFERENCE>13374470</TRAN-REFERENCE>
<ORDER>
<ORDER-NUMBER NUMBER="00241662693" REFERENCE="13374470">
<PRODUCT-CODE>DLP99022L</PRODUCT-CODE>
<START-DATE>2011-4-6 00:00:00.0</START-DATE>
<EXPIRE-DATE>2011-4-11 00:00:00.0</EXPIRE-DATE>
<MAX-USERS>1</MAX-USERS>
<ALLOWED-USERS>1</ALLOWED-USERS>
<PERSON FNAME="CART" LNAME="DUDE" RESPONSIBLITY="CART DUDE" />
</ORDER-NUMBER>
</ORDER>
</SAPPHIRE>
I have the DTO as:
public class Sapphire : IXmlSerializable
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}

Implementing IXmlSerializable is fairly tricky and potentially error prone (for example you need to make sure that your code correctly handles things like comments). For this example you should just be able to use XSD.exe to generate an appropriate class that uses attributes to control the xml serialisation.
You should use XSD.exe to generate a schema for your example xml fragment (which you ay need to tweak - if you hav an existing XSD schema then skip this step), then use it again to generate a class to use for serialisation.
If you really want to implement IXmlSerializable then try the following resources:
How to Implement IXmlSerializable Correctly (CodeProject)
Proper way to implement IXmlSerializable? (StackOverflow)

Related

Return sequence of several values in SOAP web service, using asp.NET C#

I have to implement a SOAP 1.1 web service in ASP.NET. I am given request and response examples and a glitchy wsdl spec that, when fed to the wsdl-->code wizard, doesn't produce code that gives correct response. So I am stuck with manual fixing of the auto-generated C# code.
Here is the response one of the methods must produce:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sws="http://something/WebService">
<soapenv:Header/>
<soapenv:Body>
<sws:MyActionResponse>
<sws:returnVariableOne>?</sws:returnVariableOne>
<sws:returnVariableTwo>?</sws:returnVariableTwo>
<sws:returnVariableThree>?</sws:returnVariableThree>
</sws:MyActionResponse>
</soapenv:Body>
</soapenv:Envelope>
I can't find a way how to make the <sws:MyActionResponse> contain several elements, in the specified order.
I have this code that produces only one child under the <sws:MyActionResponse> element:
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://something/WebService/MyAction", RequestElementName="MyActionRequest", RequestNamespace="http://something/WebService", ResponseNamespace="http://something/WebService", ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped, Use=System.Web.Services.Description.SoapBindingUse.Literal)]
[return: System.Xml.Serialization.XmlElementAttribute("returnVariableOne")]
public override string MyAction(string inputVariable)
{
return "Value of variable #1";
}
The response xml from it looks like this:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<MyActionResponse xmlns="http://something/WebService">
<returnVariableOne>Value of variable #1</returnVariableOne>
</MyActionResponse>
</soap:Body>
</soap:Envelope>
Well, I need three child elements there, so I am looking for the C# syntax that causes the WebMethod to return sequence of several elements in prescribed order. I am aware that I could return one complex element with complex data structures inside it, but that doesn't help because I have to match the xml response sample I am given in the spec.
I have solved this with pretty brutal approach - by editing the output stream. Until I learn of better way.
Put this code in global.asax:
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
public class XmlElementStripper : MemoryStream
{
private Stream outputStream;
private Regex reStripper = new Regex(#"</?removeThisTag>", RegexOptions.Compiled | RegexOptions.Multiline);
public XmlElementStripper(Stream output)
{
outputStream = output;
}
public override void Write(Byte[] buffer, int offset, int count)
{
// Convert the content in buffer to a string
String contentInBuffer = UTF8Encoding.UTF8.GetString(buffer);
// Strip out the tags
contentInBuffer = reStripper.Replace(contentInBuffer, String.Empty);
// Output the modified string
outputStream.Write(UTF8Encoding.UTF8.GetBytes(contentInBuffer), offset, UTF8Encoding.UTF8.GetByteCount(contentInBuffer));
}
}
In Global class (System.Web.HttpApplication):
protected void Application_PostReleaseRequestState(Object sender, EventArgs e)
{
if (Response.ContentType.StartsWith("text/xml"))
{
Response.Filter = new XmlElementStripper (Response.Filter);
}
}
Now, if the web method has this return attribute
[return: System.Xml.Serialization.XmlElementAttribute("removeThisTag")]
Then the and tags are edited out of the output stream, and when the web method returns complex type consisting of several xml-serialized fields, they are direct children of the main SOAP response message element.

How to change value(null to empty string) from query when using mybatis?

I am doing a project which uses Spring 3.1.1 and MyBatis3.0.
I am trying to change iBatis to MyBatis. However, I am struggling with resultmap.
When using iBatis, I can handle values from query like below with 'nullValue'.
<resultMap class="java.util.HashMap" id="ChannelData">
<result property="id" javaType="java.lang.String" column="CHANNEL_ID" nullValue=""/>
<result property="code" column="SELECTSCOPE" nullValue="Television"/>
</resultMap>
The problem is there no 'nullValue' in MyBatis. In addition, if the column is 'null' then mybatis never fills that element. for Example. if 'SELECTSCOPE' is null, it brings {id=aaa}. I need some data like this --> {id=aaa, code=''}. Is there anyway to handle this?
P.S.
I queries more than 20 columns. Some of them need "" when value is null, others has own default some string value.(If I use iBatis, 'nullValue' is magic keyword) I found some links which recommend to use customized type handler, but making handler more than 20 can be cause of future confusion to repair or maintaining. I need some simple way.
Thanks a lot:D
======================================================================================
I found the way to bring some null values. It needs some configuration.
make config.xml, which should contain some information about MyBatis Config DTD and Settings like below
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL MAP Config 3.1//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="callSettersOnNulls" value="true"/>
</settings>
</configuration>
now, I can get {id="aaa", code = null}. Here is additional question. How can I set default values for query? for example. if value of 'code' is null, then I want to put default String 'default'. So result should change form
{id="aaa", code=null} to {id="aaa",code="default"}. Is it possible?
Thanks~
I think I could suggest an answer for myself. But somehow it feels not efficient.
I made a TypeHandlerClass which implements the interface 'org.apache.ibatis.type.TypeHandler'.
Source code is below.
public class EmptyStringIfNull implements TypeHandler<String> {
#Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
return (rs.getString(columnName) == null) ? "" : rs.getString(columnName);
}
#Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
return (rs.getString(columnIndex) == null) ? "" : rs.getString(columnIndex);
}
#Override
public String getResult(CallableStatement cs, int columnIndex)
throws SQLException {
return (cs.getString(columnIndex) == null) ? "" : cs.getString(columnIndex);
}
#Override
public void setParameter(PreparedStatement ps, int arg1, String str,
JdbcType jdbcType) throws SQLException {
}
};
So I linked this in resultMap element 'typehandler' which looks like :
<resultMap type="map" id="channel">
<result property="id" column="CHANNEL_ID" typeHandler="StringHandler"/>
<result property="code" column="SELECTSCOPE" typeHandler="StringHandler"/>
</resultMap>
But I still got additional question. I see I can put some defaultValue in this .java code. But resultMap has many results. If every result has their own specific default value, How can handle this?
using 'if else' in java code fills inefficient, because some of them does not need to check value, they just only need to check null or not. Suggest your clever solutions :D Thanx
Indeed, the nullValue property of result map was very convenient.
If you map results to a custom type instead of a HashMap, the callSettersOnNulls setting that is false by defaults will help if concerned properties have default value:
public class ChannelData {
private String id;
private String code = "default";
}
Although it could be very tedious, you can also edit the SQL queries to handle default values using functions such as NVL / COALESCE:
SELECT channel_Id, NVL(selectScope, 'default') AS selectScope
The typeHandler you suggested has the drawback to apply to every column of the handled type and does not allow specifying different default values meeting the needs.
It can be achieved by implementing result handlers (but very verbose solution) which would consist in if-else on concerned properties (do not forget the custom result handler have to feed the result list if it is required).

ASP.NET WebService test form

Is it possible to configure default ASP.NET WebService test form to support JSON?
I mean the test form that built in in .NET framework...
Currently I have a WebService that decorated with [ScriptService], but when I testing it using built in test form page, it returns XML...I assume, this happens because test page sends Content-Type XML by default.
Thanks
EDIT (Example):
I have class:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now I have ASP.NET WebService:
[ScriptService]
public class PersonService : WebService
{
[WebMethod]
public Person GetDave()
{
Person dave = new Person();
dave.FirstName = "Dave";
dave.LastName = "Test";
return dave;
}
}
When I call this WebService from web page using jQuery AJAX, I receive JSON Person object {"FirstName":"Dave","LastName":"Test"} (not string) in JavaScript, but when I invoking this WebService from ASP.NET WebService Test Form (When I right click on ASMX file and use "Preview In Browser"),
It returns:
<?xml version="1.0" encoding="utf-8" ?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<FirstName>Dave</FirstName>
<LastName>Test</LastName>
</Person>
What want, is when I invoke the service from test page, to see the same output:
{"FirstName":"Dave","LastName":"Test"}
You can use the code below
[WebMethod(Description = "Some Description")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string FunctionName()
{
// Return JSON data
JavaScriptSerializer js = new JavaScriptSerializer();
string retJSON = js.Serialize(Object);
return retJSON;
}
And also you need to add the reference.
Update
Here is the link which will explain about extending an existing ASP.NET Web Service to support JSON
Hope that helps
It looks like I found a solution...it still not complete solution, but this is the way to go :)
In [Drive]:\[WindowsDir]\Microsoft.NET\Framework\[Version]\CONFIG folder, exists the file named DefaultWsdlHelpGenerator.aspx. This file contains the whole code needed to automatically generate test page using WSDL. Now I can use this code to write my own test page and make requests using jQuery and not using HTML form...then I can put in config file and this should work.
<webServices>
<wsdlHelpGenerator href="WSTestPage.aspx"/>
</webServices>
Maybe somewhere exists more simple/ready way to do it, but I still not found it...
Add following references first
using System.Web.Script.Services;
using System.Web.Script.Serialization;
use the below code in your method, for converting any data into JSON Data format in end
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(dr);
dr is array of DataRows from DataTable.Hope this will help You.

Register custom ConstraintValidator for existing Constraint

I use bean-validation in my project and I'd like to write a custom validator for an existing constraint annotation.
For example I have a class that represents a date/time named CustomDateTime. In a class that uses this class as for example a date of birth I'd like to annotate the field with #Past:
public class Person
{
#Past
private CustomDateTime dateOfBirth;
}
I then create a custom validator by implementing ConstraintValidator<Past, CustomDateTime>.
This however doesn't work, since the validation implementation has no knowledge of the custom validator. It then throws: javax.validation.UnexpectedTypeException: No validator could be found for type: com.example.CustomDateTime.
I know that you usually create a separate annotation like this:
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {CustomDateTimePastValidator.class})
public #interface Past
{
....
}
But that seems like double code to me ;-)
How can I register the custom validator to be used with #Past?
You can define an XML-based constraint mapping which adds your constraint validator for the existing #Past constraint:
<?xml version="1.0" encoding="UTF-8"?>
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping">
<constraint-definition annotation="javax.validation.constraints.Past">
<validated-by include-existing-validators="true">
<value>com.acme.CustomDateTimePastValidator</value>
</validated-by>
</constraint-definition>
</constraint-mappings>
Then either reference this mapping in your validation.xml:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration">
<constraint-mapping>/path/to/constraint-mapping.xml</constraint-mapping>
</validation-config>
Or you add it during bootstrapping your validator:
InputStream mappingStream = ...;
Validator validator = Validation
.byDefaultProvider()
.configure()
.addMapping( mappingStream )
.buildValidatorFactory()
.getValidator();

When I use a dynamic class for my WebService arguments, the public properties are not included

I have a dynamic Class that is a Value Object that is used to pass arguments to a WebService. It has two public properties:
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:String;
public var property2:String;
}
}
I set these two properties in one part of my application:
var myCriteria:WebServiceCriteria = new WebServiceCriteria();
myCriteria.property1 = "x";
myCriteria.property2 = "y";
Then I added other - dynamic - properties at another point in my application:
myCriteria.property3 = "z";
However, when I pass the instance to the WebService as the arguments, the original two public properties are not sent (as I can see in Fiddler), even though they have values. But, I can see them as properties of my Class instance in the debugger just prior to the send().
operation.arguments = {args: myCriteria};
operation.send(); // only property3 is sent
Why are those two properties not sent?
Here is an example of the SOAP request sent to the WebService:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<intf:webservice_controller xmlns:intf="http://childDir.parentDir">
<args xsi:type="apachesoap:Map" xmlns:apachesoap="http://xml.apache.org/xml-soap">
<item>
<key xsi:type="xsd:string">property1</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">property2</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">property3</key>
<value xsi:type="xsd:string">z</value>
</item>
</args>
</intf:webservice_controller>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This behavior is documented in Flex 3.0 manuals. See Dynamic Classes for more information. A direct quote:
[...]Methods created in this way, however, do not have access to any private properties or methods of the [example] class. Moreover, even references to public properties or methods of the [example] class must be qualified with either the this keyword or the class name.
Try to add this to your constructor :
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:String;
public var property2:String;
function WebServiceCriteria()
{
prototype.property1 = null;
prototype.property2 = null;
}
}
}
... As it seems like only the Objects properties are enumerable
I don't believe you will be able to send an object to a webservice. If you want to send an object you will need to use remote object. To use a webservice you would need to make your object into some kind of xml or soap request. like
var myCriteria:WebServiceCriteria = new WebServiceCriteria();
myCriteria.property1 = "x";
myCriteria.property2 = "y";
operation.send(<request>
<property1>{myCriteria.property1}</property1>
<property2>{myCriteria.property2}</property2>
</request>);
http://livedocs.adobe.com/flex/3/html/help.html?content=data_intro_2.html

Resources