I have a predefined xml sample which defines the requests and responses, the only part I can't get working with ServiceStack.Text.XmlSerializer is the following snippet, which is basically a list of strings.
<user>
....
<EmailPreferences>
<EmailProgram>Newsletter</EmailProgram>
<EmailProgram>Coupons</EmailProgram>
</EmailPreferences>
I tried using the example Using Structs to customise JSON, but as the title implies that didn't affect the xml serialisation.
ServiceStack uses .NET's XML DataContractSerializer under the hood. So you can decorate the models with any customizations it support. So to get something like the above you could do:
[CollectionDataContract(Name="EmailPreferences", ItemName="EmailProgram")]
public class EmailPreferences : List<string>
{
public EmailPreferences() { }
public EmailPreferences(IEnumerable<string> collection) : base(collection){}
}
Global XML Namespaces
Although you can individually add namespaces to each DataContract a better idea instead is to have all your DTOs share the same namespace, this will prevent the auto-generated and repeating namespaces from appearing in your XML.
As the ResponseStatus DTO is already under http://schemas.servicestack.net/types namespace so if you don't care what your namespace is I would leave it at that.
The easiest way to have all your DataContract's under the same namespace is to put these assembly wide attributes in your AssemblyInfo.cs for each C# namespace your DTOs are in:
[assembly: ContractNamespace("http://schemas.servicestack.net/types",
ClrNamespace = "ServiceStack.Examples.ServiceModel.Operations")]
[assembly: ContractNamespace("http://schemas.servicestack.net/types",
ClrNamespace = "ServiceStack.Examples.ServiceModel.Types")]
Related
What I should try to achieve is to have a single resource (resx) file for each supported language in net-core. I explain the problem a bit.
I have DataAnnotations on each of my entities and I need to localize the messages returned in case of errors. It seems that the default convention required by net-core is to have a different resx file for each of our entities.
This file is named accordingly to the namespace of the entity with the culture indentifier and the resx extensions. So, if I have an entity named Customers within the namespace Data.Entities, I should add a file named Data.Entities.Customers.it.resx and put all the translations for the Italian language in it. So, if I have an attribute
StringLength(50, ErrorMessage="The {0} should not be longer than {1} characters")
public string Name {get;set;}
then I add the proper translation to the Data.Entities.Customers.it.resx file.
But, if I go on another entitity like Suppliers I am forced to write another resource file named Data.Entities.Suppliers.it.resx and, of course I have
StringLength(50, ErrorMessage="The {0} should not be longer than {1} characters")
public string SupplierName {get;set;}
Now I need to write again the same translation in the proper file for the Suppliers entity. This goes on as well for other common attributes like [Required].
So I hope to have explained well my problem and my queston is: There is a way to specify a single resource file for all my entities validation messages and then write a single time the messages for the common texts?
From the docs, you can tell the framework to use a shared resource for your data annotations localisation:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization(options => {
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResource));
});
}
In the preceeding code, SharedResource is the class corresponding to the resx where your validation messages are stored. With this approach, DataAnnotations will only use SharedResource, rather than the resource for each class.
With or without this annotation, there is a property on my JPA #Entity
#Entity
public class Myentity extends ResourceSupport implements Serializable {
...
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="idrepository")
#JsonInclude(JsonInclude.Include.ALWAYS)
private MyentitySource entitysource;
...
}
that is not being mapped when I return:
#RequestMapping("/myentity/{uuid}")
public ResponseEntity<Myentity> getResourceById(#PathVariable("uuid") UUID uuid) {
Myentity result = myentityRepository.findOne(uuid);
return ResponseEntity.ok(myentityAssembler.toResource(result));
}
myentityAssembler.toResource(result) does contain this MyentitySource entitysource, but the JSON output does not.
The weirdest thing is I have another spring boot hateoas project where I am using the exact same entity, repository, controller, and assembler implementations, with the exact same dependencies and versions on my pom, and a very similar configuration (I am not defining any special jackson mappers or anything, just using the default rest/hateoas configuration), and it does work there: The MyentitySource entitysource property, which is another JPA entity extending ResourceSupport, gets serialized and included into the JSON output.
I have been a couple of hours at it already, but I am quite lost. I have verified this behavior is happening all through the application in both applications: #ManyToOne relations defined on any #Entity are being mapped and present in the JSON output on one application, but not in the other.
How can I get these fields to show up on the JSON output?
entitysource will be included if MyentitySource is not an exported entity. If it is one - what seems to be the case here - then it would be wrong to include it. Including associations could lead to sending the whole database to the client. Moreover it is a separate resource with its own URI. Consequently a link to that URI is included in the response.
CascadeType.ALL implies that Myentity is an aggregate, therefore MyentitySource should not be exported in the first place. That would solve your problem. If my assumption is wrong, then you can still use Projections to get entitysource included. I can refer you to this answer from Spring's Oliver Gierke and the relevant chapter of the documentation.
Is it possible to replace the default JSON serialization of WCF (I'm currently testing with the webHttp behaviour), and passing application/json as the MIME type. In particular, I don't like that by default every property is a key/value pair like:
{"Key":"PropertyName", "Value":"PropertyValue"}
I'm using the service only for JSON-enabled endpoints (requesting data with jQuery + WCF).
You can use a message formatter to change the serializer used to deal with JSON. The post at https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility-message-formatters shows an example on how to change the default serializer (DataContractJsonSerializer) to another one (JSON.NET).
Consider creating classes corresponding to your JSON object structure. In that case you don't have to use Dictionary<> like:
[DataContract]
public class Customer
{
[DataMember(Name="name")]
public string Name{get;set;}
[DataMember(Name="id")]
public int ID{get;set;}
}
This get serialized as:
{"name": "name-value", "id": "id-value"}
Of course, this is just an alternative to what you already have and may not be applicable.
I'm writing a REST WCF service with methods to retrieve a collection of resources and a single resource. My resources are classes based on a single abstract class. In my method to retrieve a collection I have:
[WebGet(UriTemplate = "")]
[ServiceKnownType(typeof(File)), ServiceKnownType(typeof(Text))]
List<ResourceBase> GetCollection();
and that produces XML that looks like this:
<ArrayOfResourceBase xmlns="..." xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ResourceBase i:type="Text">
...
</ResourceBase>
<ResourceBase i:type="File">
...
</ResourceBase>
</ArrayOfResourceBase>
My single object method looks like this:
[WebGet(UriTemplate = "{id}")]
[ServiceKnownType(typeof(File)), ServiceKnownType(typeof(Text))]
ResourceBase Get(string id);
and produces XML like this:
<Text xmlns="..." xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
...
</Text>
Is there a way to get those two xml parts to look the same, that is either to get rid of "i:type" tags in ArrayOf... and convert them to tag name, or force them in the single object response?
There may be several solutions here, but I can think of a few right away:
Change the custom DataContract name of the DataContract class ResourceBase to always be Text. Like so:
[DataContract(Name="Text")]
class ResourceBase
{
...
}
Plug in a DataContractSurrogate to special-case this type
Plug in a custom DataContractSerializerOperationBehavior that plugs in a custom DataContractSerializer child that special cases this type
Use XmlSerializer. See this blog post.
I have a WCF service in which I have some data contracts. I'm using web service software factory, which uses a designer to create all the message and data and other contracts, and it creates them as partial classes. When the code is regenerated the classes are recreated.
using WcfSerialization = global::System.Runtime.Serialization;
[WcfSerialization::CollectionDataContract(Namespace = "urn:CAEService.DataContracts", ItemName = "SecurityItemCollection")]
public partial class SecurityItemCollection : System.Collections.Generic.List<SecurityItem>
{
}
The data contract is a generic List of a custom class which was working fine. However I now want to add a property to this class, so I added this partial class in the same namespace:
public partial class SecurityItemCollection
{
public int TotalRecords { get; set; }
}
This seems to work fine on the service side, but when I compile and then update the service reference from the client, the class doesn't have the new property i.e. when it serialises it and recreates it on the client side, it's missing the new property. Anyone know why this is?
TIA
EDIT:
Ok this is officially driving me nuts. The only thing I can see is that it is using the CollectionDataContract attribute instead of DataContract. Does this somehow not allow data members in the class to be serialised? Why would that be? It is working fine on the service side - I can see and populate the values no problem. However when I update the service refernce on my client there's nothing, just the colelction class without the data member.
Try to add the DataMember attribute to the TotalRecords property
Ok after much searching I finally found out that this isn't allowed i.e. classes marked as CollectionDataContract can't have data members. Why this is I have no idea but it cost me several hours and a major headache. See link below:
WCF CollectionDataContract and DataMember
I know this is old but it kept coming up when I searched on this subject.
I was able to get this working by adding a "DataMemberAttribute" attribute to the property. Below is a code example.
public partial class SecurityItemCollection
{
[global::System.Runtime.Serialization.DataMemberAttribute()]
public int TotalRecords { get; set; }
}