I need to build a web service that accepts XML data.
The XML will send as the below example :
<Person>
<LegalName>
<FirstName>Ralph</FirstName>
<LastName>Anderson</LastName>
<PhoneticFirstName>rah-lf</PhoneticFirstName>
</LegalName>
<SSN>122-34-1232</SSN>
<Demographics>
<Sex>male</Sex>
<Height>502</Height>
</Demographics>
<DriversLicense>
<DriversLicenseNumber>1234</DriversLicenseNumber>
<IssuingState>CA</IssuingState>
</DriversLicense>
My understanding is I need to write something like this:
public Service () {
[WebMethod]
public void CreateRecord(XmlDocument newRecord)
{
// do stuff
}
}
How I can do that?
You could try to understand how it works and find a nice artical here:
https://msdn.microsoft.com/en-us/library/hh534080.aspx
Related
I want to serialize xml which works but I have a problem. Sometimes I have the same tag multiple times but on different places. So the order is important so I cant just stick it in an list.
For example I have the following xml:
<?xml version="1.0" encoding="utf-16"?>
<commands>
<execute>some statement</execute>
<wait>5</wait>
<execute>some statement</execute>
<wait>5</wait>
<execute>some statement</execute>
<execute>some statement</execute>
</commands>
Then my object would look something like this:
[XmlRoot(ElementName="commands")]
public class Commands {
[XmlElement(ElementName="execute")]
public List<string> Execute { get; set; }
[XmlElement(ElementName="wait")]
public List<int> Wait { get; set; }
}
If I then serialize it with the following function:
var xmlSerializer = new XmlSerializer(obj.GetType());
using (var writer = new Utf8StringWriter())
{
xmlSerializer.Serialize(writer, obj);
return writer.ToString();
}
The order will not be the same.... It would first serialize the execute tags and then the wait statements. While the order is important.
Does someone have a clue on how to tackle this problem?
Ps. changing the xml is not a solution as I'm tied to that....
Thanks in advance!
After searching for a while I tackle the problem as the following. The wait and execute are basically commands to I serialize now a list of commands. Of course the seriliazer will complain that it can't serlize this because it is a list of interfaces (ICommand) so I implemented the IXmlSerliazer so that I tell how to serliaze this.
This worked actually quite good
I have an XML file which starts something like this:-
<?xml version="1.0" encoding="UTF-8"?>
<Deal xmlns="http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<AccountingDate>2019-09-30</AccountingDate>
When I try to convert this object to XML like below, I get an error:-
private static void Prod_Error_Test()
{
string prodRequestXml = File.ReadAllText("ProdXml.xml");
var serializer = new XmlSerializer(typeof(Service.Deal));
Service.Deal request ;
var reader = new StringReader(prodRequestXml);
request = (Service.Deal)serializer.Deserialize(reader);
}
The Error Message is "There is an error in XML document (2, 2).". The Inner Exception Message is "<Deal xmlns='http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models'> was not expected."
Service.Deal is a WCF Proxy.So I may not be able to add any attributes. Can anyone suggest what to do here ?
Being a WCF proxy doesn't preclude adding attributes; in particular, it is usually a partial class, which means you can have your own separate file, with:
namespace Service
{
[XmlRoot("Deal", Namespace = "http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models")]
partial class Deal {}
}
But ultimately: if the type doesn't conveniently fit the XML: stop fighting it - create a new separate type that fits the XML and works well with XmlSerializer, and then map between the two types in your own code.
I'm relatively new to Unity and C#. Actually, I mainly look at application code and try to learn a little bit. And that's fun.
Now I've stumbled upon a problem.
I'm trying to read an XML file and continue using the data from it. That even works. But now I don't want to use all records of the XML file, but only those that have a certain ID.
Currently I do it like this:
public class Data
{
public frage[] fragen = new frage[0];
public Data () { }
public static void Write(Data data)
{
XmlSerializer serializer = new XmlSerializer(typeof(Data));
using (Stream stream = new FileStream (GameUtility.XmlFilePath, FileMode.Create))
{
serializer.Serialize(stream, data);
}
}
public static Data Fetch ()
{
return Fetch(out bool result);
}
public static Data Fetch(out bool result)
{
if (!File.Exists(GameUtility.XmlFilePath)) { result = false; return new Data(); }
XmlSerializer deserializer = new XmlSerializer(typeof(Data));
using (Stream stream = new FileStream(GameUtility.XmlFilePath, FileMode.Open))
{
var data = (Data)deserializer.Deserialize(stream);
result = true;
return data;
}
}
}
This causes, I think, that all data is stored in the corresponding variable (data). But now I want only those data sets to be transferred that have the ID 5. Is this possible with simple adjustments or do I have to think about everything?
My data set, which is created via XML, looks like this:
<?xml version="1.0"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fragen>
<frage>
<info>IRGENDEINTEXT1</info>
<antworten>
<antwort>
<info>1</info>
<korrekt>true</korrekt>
</antwort>
<antwort>
<info>2</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>3</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>4</info>
<korrekt>false</korrekt>
</antwort>
</antworten>
<id>5</id>
</frage>
<frage>
<info>IRGENDEINTEXT2</info>
<antworten>
<antwort>
<info>1</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>2</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>3</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>4</info>
<korrekt>true</korrekt>
</antwort>
</antworten>
<id>7</id>
</frage>
</fragen>
</Data>
Thank you - I hope my question is meaningful and not too unclear. Forgive my incorrect English.
You could have, but I would advise against reading only partial files all the time because that is a constant IO hit. Every time you would need to fetch a new question you get another read IO on the disk. Unless your set of questions is humongous (e.g. several hundred MB), you could read all data into your Data object at game start and then just add a helper function into your Data class that supplies you with the relevant information. E.g.
class Data {
private Frage[] fragen;
// Read a Frage by ID
public Frage QuestionById(string id) {
return this.fragen.First(it => it.id == id);
}
}
Same for the answers:
class Frage {
private Antwort[] antworten;
public Antwort GetCorrectAnswer() {
return antworten.First(it => it.korrekt);
}
}
Then in your game logic you can just call:
var aFrage = data.QuestionById("4711");
var anAntwort = aFrage.GetCorrectAnswer();
You could theoretically also use XPath to just select the XML nodes you need, but for this to work you would still need to load the whole XML document into memory in order to run your XPath over it (I'm unaware of any XPath implementation for .net that would work on streams). So you may as well just use your own data structure as I have laid out.
If you really need a huge data set and cannot load everything into memory, you should maybe look at some database solution, e.g. SQLite to store your data. This would allow you to do SQL DB queries and doesn't require you to load all the data into memory.
I agree with Jan Thomä. What you do is called deserialization (when you create an xml or json from an object you do a serialization). So you just create an object from your xml file (of course tecnically you need to read the file to deserialize it but, of course, you use a library to handle it). Once you got the data you should be able to change your object or create a modified copy with your correct data.
Usually I prefer json so I'm not sure how to do it with XmlSerializer library (link with an example in the edit part) but you should create a bean object that is a representation of your xml file
(example antwort is one of the basic object and it should have a int info variable and a korrekt boolean variable, while antworten is an object that only contains a List of antwort and so on until you reach your main object fragen that contains all your basic rappresentation of your xml bean to deserialize (to create a bean just declare your variables and Getter/Setters for those variables))
After that you just deserialize your xml as a Fragen instead of as a Data type and you can than change your object the easy way.
EDIT. To undestand it better just look the example here:
https://learn.microsoft.com/it-it/dotnet/api/system.xml.serialization.xmlserializer.deserialize?view=netframework-4.8
you got the xml and the class that representate the object OrderedItem (note: the namespace should be optional but you need to call the variable with the same name of the variable in your xml and you should need getter and setter in OrderedItem class). Once you got OrderedItem item it's easy to read/write new value to the object, serialize a new xml with new data or just create a modified copy of your object.
You should just do the same thing.
Example from the link:
using System;
using System.IO;
using System.Xml.Serialization;
// This is the class that will be deserialized.
public class OrderedItem
{
[XmlElement(Namespace = "http://www.cpandl.com")]
public string ItemName;
[XmlElement(Namespace = "http://www.cpandl.com")]
public string Description;
[XmlElement(Namespace="http://www.cohowinery.com")]
public decimal UnitPrice;
[XmlElement(Namespace = "http://www.cpandl.com")]
public int Quantity;
[XmlElement(Namespace="http://www.cohowinery.com")]
public decimal LineTotal;
// A custom method used to calculate price per item.
public void Calculate()
{
LineTotal = UnitPrice * Quantity;
}
}
public class Test
{
public static void Main()
{
Test t = new Test();
// Read a purchase order.
t.DeserializeObject("simple.xml");
}
private void DeserializeObject(string filename)
{
Console.WriteLine("Reading with Stream");
// Create an instance of the XmlSerializer.
XmlSerializer serializer =
new XmlSerializer(typeof(OrderedItem));
// Declare an object variable of the type to be deserialized.
OrderedItem i;
using (Stream reader = new FileStream(filename, FileMode.Open))
{
// Call the Deserialize method to restore the object's state.
i = (OrderedItem)serializer.Deserialize(reader);
}
// Write out the properties of the object.
Console.Write(
i.ItemName + "\t" +
i.Description + "\t" +
i.UnitPrice + "\t" +
i.Quantity + "\t" +
i.LineTotal);
}
}
xml
<?xml version="1.0"?>
<OrderedItem xmlns:inventory="http://www.cpandl.com" xmlns:money="http://www.cohowinery.com">
<inventory:ItemName>Widget</inventory:ItemName>
<inventory:Description>Regular Widget</inventory:Description>
<money:UnitPrice>2.3</money:UnitPrice>
<inventory:Quantity>10</inventory:Quantity>
<money:LineTotal>23</money:LineTotal>
</OrderedItem>
I've been using xsd2code v3.4.
So far I'm pretty close to getting it to work, however I'm facing one glaring issue and I can't seem to find any solutions. When my XML gets generated after I serialize my object, it's adding an additional complex type that is named exactly like the class. This is what I currently get. Notice how it's adding an unnecessary collection right after an order line:
<?xml version="1.0" encoding="utf-8"?>
<CORE_PO_INBOUND_V2 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<INTEGRATION_MESSAGE_CONTROL>
<ACTION>FULL_UPDATE</ACTION>
<COMPANY_CODE>COMPANY</COMPANY_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<MESSAGE_TYPE>INBOUND_ENTITY_INTEGRATION</MESSAGE_TYPE>
<USERID>COMPANY</USERID>
<RECEIVER>TA15</RECEIVER>
<SENDER>COMPANY</SENDER>
<BATCH_ID>1234</BATCH_ID>
<BUS_KEY>
<ORG_CODE>COMPANY</ORG_CODE>
<PO_NUMBER>1234</PO_NUMBER>
</BUS_KEY>
</INTEGRATION_MESSAGE_CONTROL>
<PURCHASE_ORDER_HEADER>
<CTRY_OF_EXPORT>TR</CTRY_OF_EXPORT>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<INCOTERM_CODE>011</INCOTERM_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<SOURCE_TX_ID>THING</SOURCE_TX_ID>
<PO_NUMBER>1234</PO_NUMBER>
<PURCHASE_ORDER_LINE>
<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>
<BUSINESS_UNIT>BCA</BUSINESS_UNIT>
<COMMERCIAL_UOM>EA</COMMERCIAL_UOM>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<DEPARTMENT>602</DEPARTMENT>
<LINE_ID>1</LINE_ID>
</CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>
</PURCHASE_ORDER_LINE>
<PURCHASE_ORDER_HEADER_PARTNER>
<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_HEADER_PARTNER>
<REF_RESOLUTION_PARTNER>Stuff</REF_RESOLUTION_PARTNER>
</CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_HEADER_PARTNER>
</PURCHASE_ORDER_HEADER_PARTNER>
</PURCHASE_ORDER_HEADER>
</CORE_PO_INBOUND_V2>
This is what I actually want:
<?xml version="1.0" encoding="utf-8"?>
<CORE_PO_INBOUND_V2 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<INTEGRATION_MESSAGE_CONTROL>
<ACTION>FULL_UPDATE</ACTION>
<COMPANY_CODE>COMPANY</COMPANY_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<MESSAGE_TYPE>INBOUND_ENTITY_INTEGRATION</MESSAGE_TYPE>
<USERID>COMPANY</USERID>
<RECEIVER>TA15</RECEIVER>
<SENDER>COMPANY</SENDER>
<BATCH_ID>1234</BATCH_ID>
<BUS_KEY>
<ORG_CODE>COMPANY</ORG_CODE>
<PO_NUMBER>1234</PO_NUMBER>
</BUS_KEY>
</INTEGRATION_MESSAGE_CONTROL>
<PURCHASE_ORDER_HEADER>
<CTRY_OF_EXPORT>TR</CTRY_OF_EXPORT>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<INCOTERM_CODE>011</INCOTERM_CODE>
<ORG_CODE>COMPANY</ORG_CODE>
<SOURCE_TX_ID>THING</SOURCE_TX_ID>
<PO_NUMBER>1234</PO_NUMBER>
<PURCHASE_ORDER_LINE>
<BUSINESS_UNIT>BCA</BUSINESS_UNIT>
<COMMERCIAL_UOM>EA</COMMERCIAL_UOM>
<CTRY_OF_IMPORT>US</CTRY_OF_IMPORT>
<CURRENCY_CODE>USD</CURRENCY_CODE>
<DEPARTMENT>602</DEPARTMENT>
<LINE_ID>1</LINE_ID>
</PURCHASE_ORDER_LINE>
<PURCHASE_ORDER_HEADER_PARTNER>
<REF_RESOLUTION_PARTNER>Stuff</REF_RESOLUTION_PARTNER>
</PURCHASE_ORDER_HEADER_PARTNER>
</PURCHASE_ORDER_HEADER>
</CORE_PO_INBOUND_V2>
Is there some setting I'm using incorrectly? I have it set to work with List collections. Seems as if this problem only exists for collections of a class that's generated from this tool.
Edit: Adding some snippets of the designer class that gets generated by xsd2code. Note that this file is pretty large (almost 10k lines...), so I'm not going to post the whole thing here, but rather the chunks that pertain to the purchase order line collections of elements:
public partial class CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADER : EntityBase<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADER>
{
private List<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE> pURCHASE_ORDER_LINEField;
public List<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE> PURCHASE_ORDER_LINE
{
get
{
if ((this.pURCHASE_ORDER_LINEField == null))
{
this.pURCHASE_ORDER_LINEField = new List<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>();
}
return this.pURCHASE_ORDER_LINEField;
}
set
{
this.pURCHASE_ORDER_LINEField = value;
}
}
}
public partial class CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE : EntityBase<CORE_PO_INBOUND_V2PURCHASE_ORDER_HEADERPURCHASE_ORDER_LINE>
{
private System.Nullable<decimal> aREAField;
private bool aREAFieldSpecified;
private string aREA_UOMField;
...
}
I think I found a solution. I spoke with a colleague whom had done something similar. He said he used the native "xsd" and not "xsd2code". We did a compare on what got generated and noticed that on the arrays (in my case, I use lists...), he had the following annotation:
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)]
Is there a way to trigger this same annotation via xsd2code? Looks like without it extra elements get generated upon executing the serializer.
Is it possible to create an C# web service which returns multiple strings, without generating a complex type? (Because my client can't handle complex types and I need to return exactly 2 strings with the names: "wwretval" and "wwrettext")
I've already tried to create a struct or a class or do it with out params, but it always generated a complex type in the WSDL.
Could you send them as an XML blob or, failing that, pack the strings into a single string separater by a nonprinting character such as \n then split them at the other end?
The latter is not exactly elegant but could work.
Since you can't change the client perhaps you can fool it by forcing Soap to use RPC mode with literal binding:
namespace WebTest
{
public struct UploadResponse
{
public string wwretval;
public string wwrettext;
}
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.None)]
[System.ComponentModel.ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
[SoapRpcMethod(ResponseElementName = "UploadResponse",Use=SoapBindingUse.Literal)]
[WebMethod]
public UploadResponse Upload()
{
UploadResponse ww = new UploadResponse();
ww.wwretval = "Hello";
ww.wwrettext = "World";
return ww;
}
}
}
That will generate a response with two strings inside of an UploadResponse element. You can then generate a fake wsdl as described here: How do I include my own wsdl in my Webservice in C#
You can send the 2 string separated by a ';' . And the client split the string.
(Cowboy coder from hell)