Read values from XDocument - asp.net

I need to enumerate an XML (XDocument) document as such...
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<applied>
<ah ID="8298" userId="87459" roleId="2700" />
<ah ID="8300" userId="87459" roleId="2699" />
<ah ID="8299" userId="87460" roleId="2700" />
</applied>
...and extract all the values of userID and roleID into two separate StringBuilder objects.
I've been messing about with Linq for the first time, but am struggling, and would appreciate some help please (this is a VB webforms app). I have seen lots of Linq example that get a single value, but not two at the same time in the manner I need.
Also, this XML file could be up to 100,000 rows in length. Is Linq more memory efficient than, say, a FOR EACH loop? If not, could someone please provide a FOR EACH example?
Thanks in advance.
Update: The result would be:
Userids = "87459,87459,87460"
roleIds = "2700,2699,2700"

First, you need to get the xml from your file system into memory. This is called deserialization. My preferred method of doing this is using a model for the xml, which is just a class with some attributes to tell the xml serializer how to parse the file:
<XmlRoot("applied")> _
Public Class Applied
<XmlElement("ah")> _
Public Property AhList As List(Of Ah)
End Class
Public Class Ah
<XmlAttribute("ID")> _
Public Property ID As Integer
<XmlAttribute("userID")> _
Public Property UserID As Integer
<XmlAttribute("roleID")> _
Public Property RoleID As Integer
End Class
Note that the XmlAttribute tells the serializer exactly what the attribute is called in xml, but you may use any name for it in the model, i.e. <XmlAttribute("userID")> Public Property UserID As Integer.
Now that you have this model, you need to deserialize from xml.
Dim appls As New Applied()
Dim serializer As XmlSerializer = Nothing
serializer = New XmlSerializer(GetType(Applied))
appl = serializer.Deserialize(xd.CreateReader()) ' xd is your XDocument
Now you should have all the xml deserialized into the appls object. This object has a property, according to the model, called AhList, which is a list of all the elements in your xml. Since List(Of Ah) is IEnumerable, you can use LINQ on it.

Related

Newbie to using Classes Properly - How to Set a Property or Class Value to return of a Function

I have been building various web based programs and things for a while, but am quite new to .NET and doing things "properly." As I am completely self taught, with help from sites like this and so on, my understanding of fundamentals is limited.
So, I have a series of functions which return data that I want depending on parameters put in, this is very basic stuff, and obviously all works. However, I am trying to make it easier to call these functions by using Classes.
So, say I have a function which returns a populated DropDownList converted to an HTML string
Function GetList(ListRequired as String) as String
' Do stuff to return a dropdownlist whos contend is determined by `ListRequired`, converted to an HTML string
End Function
In this example, it works fine, but to use it I must know what to enter for "ListRequired" to get what I want.
So, let's say, the options for the ListRequired para are "mastercategory", "brandlist", "priceranges" to return a different set of lists - each option would send the code off the retrieve information from a database and return accordingly.
Suppose I want a third party developer to be able to call this function with the most basic amount of "instruction" required, and not even have to tell him the list of available ListRequired by making it available as a Class.
Public Class DropDownLists
Public Property MasterCategory
Public Property BrandList
Sub New()
Me.MasterCategory = HTMLControls.RenderSearchFilters("mastercategory")
Me.BrandList = HTMLControls.RenderSearchFilters("brandList")
End Sub
End Class
A developer can then call this from Visual Studio/VWD etc very simply:
Dim dd As New DropDownLists
Dim list1Html as String = dd.MasterCategory
Dim list2Html as String = dd.BrandList
Because VWD etc creates all the handy helpers and displays which properties the Class exposes, it is very easy to use this code without have to constantly refer to a manual.
However... when creating a new instance of the Class:
Dim dd As New DropDownLists
This will cause the server to process all the functions within the class which create the Properties, which would be desperately inefficient if there are lots of properties.
So I have tried using my own interpretation of the logic and written this:
Public Class DropDownLists
Shared Property Master
Shared Property Brand
Sub New()
End Sub
Public Class MasterCategory
Sub New()
DropDownLists.Master = HTMLControls.RenderSearchFilters("mastercategory")
End Sub
End Class
Public Class BrandList
Sub New()
DropDownLists.Brand = HTMLControls.RenderSearchFilters("brandList")
End Sub
End Class
End Class
Hoping I'd be able to create the HTML for a Master Category drop down like:
Dim dd as New DropDownLists.MasterCategory
But that doesn't work, and upon reflection I think I can see why... it's not returning the string, but creating a new type.
So... my question is...
What is the correct way to achieve what I am looking for, which is, to be able to create these string outputs by typing
Dim dd As New DropDownLists
Dim list1Html as String = dd.MasterCategory
Dim list2Html as String = dd.BrandList
Without having to pass potentially unknown string parameters, or causing ALL properties to be created each time the DropDownLists Class is created, ie, only run the code for the output I need.
I'm expanding my comment to give you a clearer idea of what I meant:
Public Class DropDownLists
Enum ListType
Undefined
MasterCategory
Brandlist
End Enum
Public Shared Function GetList(ListRequired As ListType) As String
Select Case ListRequired
Case ListType.Brandlist
Return . . .
Case ListType.MasterCategory
Return . . .
Case ListType.Undefined
Throw New . . . .
End Select
End Function
End Class

How to POST XML using Fiddler to ASP.NET WebAPI

Given the following ASP.NET WebAPI, I am trying to send a test POST using Fiddler, but can't get it to work. Whatever I send, I always just see the No data sent to service message.
Imports System.Web.Http
Imports System.Net.Http
Imports System.Net
Namespace HelloWebApiDemo
Public Class MyApiController
Inherits ApiController
Public Function [Get]() As HttpResponseMessage
Return Request.CreateResponse(HttpStatusCode.OK, "Hello")
End Function
Public Class MyXmlData
Public Property UserName As String
Public Property Password As String
Public Property SomeData As String
End Class
Public Function Post(<FromBody> x As MyXmlData) As HttpResponseMessage
If x Is Nothing Then
Return Request.CreateResponse(HttpStatusCode.InternalServerError, "No data sent to service")
End If
Dim u As String = String.Empty
Dim p As String = String.Empty
Dim d As String = String.Empty
If Not String.IsNullOrEmpty(x.UserName) Then
u = x.UserName
End If
If Not String.IsNullOrEmpty(x.Password) Then
p = x.Password
End If
If Not String.IsNullOrEmpty(x.SomeData) Then
d = x.SomeData
End If
Return Request.CreateResponse(HttpStatusCode.OK, String.Format("You posted {0}, {1} with a string that is {2} characters in length", u, p, d.Length.ToString))
End Function
End Class
End Namespace
In Fiddler, my POST looks like this:
Can anyone please advise on what I'm doing wrong? I used Content-Type: text/xml hoping that ASP.NET would be able to decipher it correctly.
Update
New screen grab following input:
Keep the Content-Type: text/xml request header and change the XML to like this.
<MyApiController.MyXmlData
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/HelloWebApiDemo.HelloWebApiDemo">
<Password>somepassword</Password>
<SomeData>somedata</SomeData>
<UserName>bob</UserName>
</MyApiController.MyXmlData>
Final XML snippet that was successfully deserialized:
<MyApiController.MyXmlData
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/_WebApplication1.HelloWebApiDemo">
<Password>Password</Password>
<SomeData>Data here</SomeData>
<UserName>Some Username</UserName>
</MyApiController.MyXmlData>
_WebApplication1 is the name of the solution. That must've been what was missing.
For those who have the ability to do this when faced with the same issue, another option is to use JSON. I am a huge fan of XML, but unfortunately, it seems things are far more stacked against XML's usage in scenarios like this, because in this case, the standard WebApi parser requires all that hideous extra stuff, including intimate knowledge of backend namespaces and types, as well as a full XML spec namespace (* see rant below).
{
"SomeData": "R2D2",
"UserName": "Johny",
"Password": "password",
"Num": 1013,
"IsCool": true
}
*( Rant, please ignore if you want to: Why require this, oh ye good gentleman who made WepApi and other such frameworks? why not allow the XML to be liberated, freely receivable without all this 'ugly'? If included, it could still specify something useful, if one needs to, but why must the rest of us include this ugliness? It seems things have been (arbitrarily, though perhaps more for historical baggage reasons, sorry you poor XML creature, you're stuck with yesterday's baggage though it need not be) stacked against XML's usage, including that, even with all this, a standard parser likely would not allow you to post the XML with the values as XML attributes. )
You can mark your entity class with DataContract attribute setting namespace to an empty string:
<DataContract(Namespace := "")>
Public Class MyXmlData
End Class
After that xmlns parameters will not be necessary.

ASP.NET REST service without escape characters in JSON results

I'm calling a REST service searchFavoriteCompany that in turn calls another service A.
Service A already returns JSON. But since my return type of searchFavoriteCompany my search results are then returned with escape characters:
"{\"responseHeader\":{\"status\":0,\"QTime\":2,\"params\":{\"facet\":\"false\",\"fl\":\"id,title,friendlyurl,avatar,locpath,objectid,objecttype\",\"indent\":\"off\",\"start\":\"0\",\"q\":\"title_search:*castle*\",\"wt\":\"json\",\"fq\":\"userid:\\\"C325D42C-A777-4275-BDD2-D7810A8AB9AB\\\"\",\"rows\":\"10\",\"defType\":\"lucene\"}},\"response\":{\"numFound\":2,\"start\":0,\"docs\":[{\"title\":\"Castle A\",\"objecttype\":1,\"friendlyurl\":\"castle-a\",\"avatar\":\"6_887_castle-a.JPG\",\"objectid\":6},{\"title\":\"Castle B\",\"objecttype\":1,\"friendlyurl\":\"castle-b\",\"avatar\":\"794_360_13j-Castle-by-night.jpg\",\"objectid\":794}]}}\u000a"
I don't know how to make sure my JSON results are returned without these escape characters.
Imyservice.vb
Namespace RestService
<ServiceContract()>
Public Interface Imyservice
<OperationContract()> _
_
Function searchFavoriteCompany(ByVal q As String, ByVal uuid As String) As String
End Interface
End Namespace
iservice.svc.vb
Namespace RestService
Public Class iservice
Implements Imyservice
Public Function searchFavoriteCompany(ByVal q As String, ByVal uuid As String) As String Implements Imyservice.searchFavoriteCompany
Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost/getjson") <---- this service already returns JSON data
Dim Resp As HttpWebResponse = req.GetResponse()
Dim reader As StreamReader = New StreamReader(Resp.GetResponseStream)
Dim responseString As String = reader.ReadToEnd()
Return responseString
End Function
End Class
End Namespace
Instead of returning the responsestring I also tried:
HttpContext.Current.Response.Write(responseString)
return ""
But then I get the error 'Object reference not set to an instance of an object'
And I checked: responsestring contains a value, so that is not the problem.
If you can't pass the output directly to the response, your best bet is to go back to traditional methods, and return a type-safe object from your method. You've got the JSON that the original service passes you, and your end-goal is to return that exact object back to your caller.
Assuming you've got a complete JSON object in your example - you can first go to json2csharp to take your JSON and create classes out of it. It uses C#, but you can easily convert it to VB using something like Telerik's Code Converter.
If you follow these steps, you'll end up with several classes, including one called RootObject, which contains properties for responseHeader and response, and the other classes needed. You can rename the classes as you see fit, and put them into proper namespaces - just leave the property names alone, so they match the JSON.
Once you've got this RootObject class (or whatever you rename it to), you can deserialize the JSON you got from the original service into an object of type RootObject, then your WebAPI method would simply return a RootObject. Just like any other WCF call, the framework will take care of serializing that object back into JSON (or XML) for the caller.

Trying to convert an entire structure to a string in ASP.NET

I'm using a structure as follows:
Structure USERREC
Public UserRecID As Integer
Public CompanyRecID As Integer
Public Username As String
Public LoggedInTime As DateTime
Public UserIsLocked As Boolean
Public UserFirstName As String
Public UserLastName As String
Public UserEmail As String
... deleted for simplicity ...
End Structure
When I instantiate this type into a variable (Dim ThisUserRec As USERREC = (data routine) ), I can break the code and go to the immediate window and simply type "? ThisUserRec" and I see a nice display of every element in the structure
? ThisuserRec
{USERREC}
UserFirstName: "Alex "
UserIsDeleted: False
UserIsLocked: False
UserIsOwnerMgr: False
I removed elements for security and simplicity purposes in this example.
So, I'd love to save the entire structure into my audit table as a string. I was hoping there was some easy way like
Dim ThisUserRecString as String = ThisUserRec
but of course, I know that won't work.
Anybody know a quick way to dump the contents of a structure into a string? Or an HTML table?
You can easely serialize to JSon with DataContractJsonSerializer.
This guy has a great example: How can I make DataContractJsonSerializer serialize an object as a string?
I converted it to VB
Private Function ToJson(Of T)(data As T) As String
Dim serializer As New DataContractJsonSerializer(GetType(T))
Using ms As New MemoryStream()
serializer.WriteObject(ms, data)
Return Encoding.[Default].GetString(ms.ToArray())
End Using
End Function
Just call it like this
ToJson(Of USERREC)(object)
Don't forget to import the libraries
Imports System.Runtime.Serialization.Json
Imports System.IO
Imports System.Text
And make it serializable
<Serializable()> _

ASP.Net JSON Web Service Post Form Data

I have a ASP.NET web service decorated with System.Web.Script.Services.ScriptService() so it can return json formatted data. This much is working for me, but ASP.Net has a requirement that parameters to the web service must be in json in order to get json out.
I'm using jquery to run my ajax calls and there doesn't seem to be an easy way to create a nice javascript object from the form elements. I have looked at serialiseArray in the json2 library but it doesn't encode the field names as property name in the object.
If you have 2 form elements like this
<input type="text" name="namefirst" id="namefirst" value="John"/>
<input type="text" name="namelast" id="namelast" value="Doe"/>
calling $("form").serialize() will get you the standard query string
namefirst=John&namelast=Doe
calling JSON.stringify($("form").serializeArray()) will get you the (bulky) json representation
[{"name":"namefirst","value":"John"},{"name":"namelast","value":"Doe"}]
This will work when passing to the web service but its ugly as you have to have code like this to read it in:
Public Class NameValuePair
Public name As String
Public value As String
End Class
<WebMethod()> _
Public Function GetQuote(ByVal nvp As NameValuePair()) As String
End Function
You would also have to wrap that json text inside another object nameed nvp to make the web service happy. Then its more work as all you have is an array of NameValuePair when you want an associative array.
I might be kidding myself but i imagined something more elegant when i started this project - more like this
Public Class Person
Public namefirst As String
Public namelast As String
End Class
which would require the json to look something like this:
{"namefirst":"John","namelast":"Doe"}
Is there an easy way to do this? Obviously it is simple for a form with two parameters but when you have a very large form concatenating strings gets ugly. Having nested objects would also complicate things
The cludge I have settled on for the moment is to use the standard name value pair format stuffed inside a json object. This is compact and fast
{"q":"namefirst=John&namelast=Doe"}
then have a web method like this on the server that parses the query string into an associate array.
<WebMethod()> _
Public Function AjaxForm(ByVal q As String) as string
Dim params As NameValueCollection = HttpUtility.ParseQueryString(q)
'do stuff
return "Hello"
End Sub
As far a cludges go this one seems reasonably elegant in terms of amount of code, but my question is: is there a better way? Is there a generally accepted way of passing form data to asp.net web/script services?
You are just having a formatting crisis.
To properly call this code:
Public Class NameValuePair
Public name As String
Public value As String
End Class
<WebMethod()> _
Public Function GetQuote(ByVal nvp As NameValuePair()) As String
End Function
You need to send a json string that looks like this:
'{"nvp": {"name": "john", "value": "foo"}}'
Do not use jQuery to serialize ScriptService arguments. Use the standard json2.js.
Try this:
Form
...
<input type="text" name="name" id="name" value="John"/>
<input type="text" name="value" id="value" value="Foo"/>
...
Script:
var myNvp = {name: $('#name').val(), value:$('#value').val()};
var data = JSON.stringify({nvp: myNvp});
// data is what you post to the service.
I just wrote this off the top of my head but it looks right to me.
Let me know if you have any other questions.
I havn't found anything better than what i was already thinking with passing the serialized string as a parameter.
input:
{"q":"namefirst=John&namelast=Doe"}
webservice:
<WebMethod()> _
Public Function AjaxForm(ByVal q As String) as string
Dim params As NameValueCollection = HttpUtility.ParseQueryString(q)
'do stuff
return "Hello"
End Sub
This seems the cleanest and simplest option

Resources