We have a Windows Store application that communicates with our server using XML for requests / responses and are serialized with the XmlSerializer. The issue we are encountering is that one of our types can contain arbitrary XML as one of its properties. In non WinRT applications, the usage would have been.
public sealed ItemExtension {
[XmlAttribute("source")]
public string Source {get;set;}
[XmlAnyElement]
public XmlElement[] Data {get;set;}
}
This would allow us to have XML in our database like
<extension source="foo"><randomXml><data/></randomXml></extension>
In WinRT, XmlElement is not included, System.Xml.XmlElement does not exist and the Windows.Data.Xml.Dom.XmlElement is not compatible. Documentation mentions XElement, but XElement is not a supported WinRT type so the WinRT project won't compile if I try using it.
Is this a bug with Windows Store applications or is there a sufficient work around?
Thanks.
So far I've only found a hack to get this working. If we use
[XmlAnyElement]
public object Data {get;set;}
This will properly will properly deserialize existing data. In the debugger when inspecting it, it is of type System.Xml.XmlElement, which isn't exposed in WinRT. So there's no way to directly set this. Since we figured out that XmlSerializer can instantiate and access System.Xml.XmlElement, we use it to handle setting it by taking in an object/xml snippet, wrapping it in container xml for a wrapper type that contains [XmlAnyElement] and calling Deserialize on it to have the XmlSerializer instantiate an XmlElement, which can then be set on the target object you wish to serialize.
For getting data, since trying to read this property throws an exception in the UI layer, and trying to access InnerXml/OuterXml throw an exception as well, we are left with using the XmlSerializer to Serialize the XmlElement back into a string, and then can use that however you want.
public sealed class XmlAnyElementContainer
{
[XmlAnyElement]
public object Data { get; set; }
}
public void SetData(object extensionObject)
{
var objectSerializer = new XmlSerializer(extensionObject.GetType());
var settings = new XmlWriterSettings()
{
Indent = false,
OmitXmlDeclaration = true
};
var sb = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(sb, settings))
{
objectSerializer.Serialize(xmlWriter, extensionObject);
}
string objectXml = sb.ToString();
string newXml = "<XmlAnyElementContainer>" + objectXml + "</XmlAnyElementContainer>";
var xmlAnySerializer = new XmlSerializer(typeof(XmlAnyElementContainer));
using (var sr = new StringReader(newXml))
{
[TargetPropertyToSerialize] = (xmlAnySerializer.Deserialize(sr) as XmlAnyElementContainer).Data;
}
}
Related
** FIXED **
All I had to do is add an apostrophe before and after each argument index,
i.e, change:
#Query(value = "{'type': 'Application','name': ?0,'organizationId': ?1}", fields = "{_id:1}")
To:
#Query(value = "{'type': 'Application','name': '?0','organizationId': '?1'}", fields = "{_id:1}")
===================
I recently upgraded my MongoDB and my Spring-Data-MongoDB Driver.
I used to access my MongoDB through mongoRepository using this code:
#Query(value = "{'type': 'Application','name': ?0,'organizationId': ?1}", fields = "{_id:1}")
Policies findPolicyByNameAndOrganizationId(String name, String organizationId);
Where Policies is the object I want to consume.
After performing an update to Spring, I get the following Error now when accessing the method above:
org.bson.json.JsonParseException: Invalid JSON number
I fear this is because I use Spring's MongoCoverter (in the case of this specific object only) to map documents to object.
Here's is my Reader Converter:
public class ApplicationPolicyReadConverotor implements Converter<Document, ApplicationPolicy > {
private MongoConverter mongoConverter;
public ApplicationPolicyReadConverotor(MongoConverter mongoConverter) {
this.mongoConverter = mongoConverter;
}
//#Override
public ApplicationPolicy convert(Document source) {
ApplicationPolicyEntity entity = mongoConverter.read(ApplicationPolicyEntity.class, source);
ApplicationPolicy policy = new ApplicationPolicy();
addFields(policy, entity);
addPackages(policy, entity);
return policy;
}
And here's is my Writer Converter:
public class ApplicationPolicyWriteConvertor implements Converter<ApplicationPolicy, Document>{
private MongoConverter mongoConverter;
public ApplicationPolicyWriteConvertor(MongoConverter mongoConverter) {
this.mongoConverter = mongoConverter;
}
#Override
public Document convert(ApplicationPolicy source) {
System.out.println("mashuWrite");
ApplicationPolicyEntity target = new ApplicationPolicyEntity();
copyFields(source, target);
copyPackages(source, target);
Document Doc = new Document();
mongoConverter.write(target, Doc);
return Doc;
}
I checked Spring reference (2.0.2) regarding MongoConverter and how it works and at this stage I think I'm doing it correctly.
Other object who do not use mapping/conversions suffer no problems.
Same did this Object (ApplicationPolicy) untill I upgraded my mongo and my spring driver.
My mongodb is 3.4.10 and Spring data mongo driver is 2.0.2.
Here's the code that initializes the MappingMongoCoverter Object:
(Adds my custom Converters).
SimpleMongoDbFactory simpleMongoDbFactory = new SimpleMongoDbFactory(client, dbName);
DefaultDbRefResolver defaultDbRefResolver = new DefaultDbRefResolver(simpleMongoDbFactory);
MongoMappingContext mongoMappingContext = new MongoMappingContext();
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(defaultDbRefResolver,
mongoMappingContext);
mappingMongoConverter.setMapKeyDotReplacement("_dot_");
// Adding custom read and write converters for permission policy.
mappingMongoConverter.setCustomConversions(new MongoCustomConversions(Arrays.asList(
new ApplicationPolicyWriteConvertor(mappingMongoConverter), new ApplicationPolicyReadConverotor(
mappingMongoConverter))));
mappingMongoConverter.afterPropertiesSet();
final MongoTemplate template = new MongoTemplate(simpleMongoDbFactory, mappingMongoConverter);
return template;
I know for sure that ReaderConverter WORKS legit (at least in some cases) since other aspects of the software use the custom ReaderConverter I've written and it works as expected.
Also when using debug mode (Intellij) I do not reach to the conversion code block when invoking the following:
#Query(value = "{'type': 'Application','name': ?0,'organizationId': ?1}", fields = "{_id:1}")
Policies findPolicyByNameAndOrganizationId(String name, String organizationId);
So basically I'm kinda clueless. I have a sense my converter Implementation is messy but couldn't fix it..
I am used to JAX-RS and would like to have similar comfort when sending requests using Spring MVC and working with the responses, i.e. on the client side inside my tests.
On the server (controller) side I'm quite happy with the automatic conversion, i.e. it suffices to just return an object instance and have JSON in the resulting HTTP response sent to the client.
Could you tell me how to work around the manual process of converting objectInstance to jsonString or vice versa in these snippets? If possible, I'd also like to skip configuring the content type manually.
String jsonStringRequest = objectMapper.writeValueAsString(objectInstance);
ResultActions resultActions = mockMvc.perform(post(PATH)
.contentType(MediaType.APPLICATION_JSON)
.content(jsonStringRequest)
)
String jsonStringResponse = resultActions.andReturn().getResponse().getContentAsString();
Some objectInstanceResponse = objectMapper.readValue(jsonStringResponse, Some.class);
For comparison, with JAX-RS client API I can easily send an object using request.post(Entity.entity(objectInstance, MediaType.APPLICATION_JSON_TYPE) and read the response using response.readEntity(Some.class);
if you have lot's of response objects, you could create some generic JsonToObject mapper-factory. It could be then used to detect the object type from a generic response (all response objects inherit from the same generic class) and respond/log properly from a bad mapping attempt.
I do not have a code example at hand, but as a pseudocode:
public abstract GenericResponse {
public String responseClassName = null;
// get/set
}
In the server code, add the name of the actual response object to this class.
The JsonToObject factory
public ConverterFactory<T> {
private T objectType;
public ConverterFactory(T type) {
objectType = type;
}
public T convert(String jsonString) {
// Type check
GenericResponse genResp = mapper.readValue(result.getResponse().getContentAsString(),
GenericResponse.class);
if (objectType.getClass().getSimpleName().equals(genResp.getResponseClassName())) {
// ObjectMapper code
return mapper.readValue(result.getResponse().getContentAsString(),
objectType.class);
} else {
// Error handling
}
}
}
I think this could be extended to be used with annotation to do more automation magic with the response. (start checking with BeanPostProcessor)
#Component
public class AnnotationWorker implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(final Object bean, String name) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), field -> {
// make the field accessible if defined private
ReflectionUtils.makeAccessible(field);
if (field.getAnnotation(MyAnnotation.class) != null) {
field.set(bean, log);
}
});
return bean;
}
}
The above code snippet is copied from my current project and it injects to fields, you need to change it so, that it works for methods, eg ... where you may need it.
Having this all implemented may be tricky and can't say it necessarily works even, but it's something to try if you don't mind a bit of educative work.
I am passing a json object to the client side from java object with a time and value as attributes with gson
this.template.convertAndSend("/topic/123", gson.toJson(object, type));
and on the client side i have the following code where the json object data is stored in the body of the payload but I am unable to access the properties with obj.time or obj.value, it tells me undefined after it is parsed, I tried showing the entire 'obj' itself and the format seems fine however:
var subscription_callback1 = function(payload) {
var obj = JSON.parse(payload.body);
alert(obj);
};
output with alert(obj)
{"time":"3:00:34","value":"7989797"}
Nevermind solved. Since I am transfering STOMP protocol messages with the Spring 4 framework. I opted to use the Jackson2 message converter instead of directly using gson and it seems to work
#Configuration
#EnableWebSocketMessageBroker
public class MessageBrokerConfigurer extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
messageConverters.add(new MappingJackson2MessageConverter());
return true;
}
then i directly put my java object into the send function instead of using gson to convert it as above
this.template.convertAndSend("/topic/123", event)
In a Windows Store app I can only store WinRT types in the ApplicationSettings, according to the documentation. For roamed settings that should be held together I can use ApplicationDataCompositeValue. Trying to store an instance of an own class or struct results in an Exception with the message " WinRT information: Error trying to serialize the value to be written to the application data store. Additional Information: Data of this type is not supported". The term "trying to serialize" indicates that there must be some way so serialize a type for the application data API.
Does anyone know how I could achieve that?
I tried DataContract serialization but it did not work.
I think custom/own types are not supported.
See http://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx:
"The Windows Runtime data types are supported for app settings."
But you can serialize your objects to XML and save as string... (see code below)
public static string Serialize(object obj)
{
using (var sw = new StringWriter())
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(sw, obj);
return sw.ToString();
}
}
public static T Deserialize<T>(string xml)
{
using (var sw = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(sw);
}
}
https://github.com/MyToolkit/MyToolkit/blob/master/src/MyToolkit/Serialization/XmlSerialization.cs
Check out this class too:
https://github.com/MyToolkit/MyToolkit/wiki/XmlSerialization
Disclaimer: The above links are from my project
I can't seem to find anything in the OpenRasta docs or tutorials that shows how to use arbitrary JSON objects (i.e. objects not predefined using C# classes) for both receiving from and responding back to the client.
One way to do it would be to use JsonValue and write a custom codec that would just use the (de)serialization features provided by JsonValue. That should be pretty straightforward and less than 50 lines of code, but I wondered if there isn't anything built into OpenRasta?
(One downside of JsonValue is that MS has not yet released it, so you can't yet deploy it to customers (see 1. "Additional Use Rights"). But in cases where that matters, any other Json library, like Json.NET can be used.)
I have written, like most people, a very simple codec that supports dynamics as inputs and outputs to handlers using json.net. You can also register that codec with an anonymous type and it works brilliantly. You end up with this:
public object Post(dynamic myCustomer) {
return new { response = myCustomer.Id };
}
I just implemented a JSON codec using JsonFx. It goes like this:
using System.IO;
using System.Text;
using JsonFx.Json;
namespace Example
{
[global::OpenRasta.Codecs.MediaType("application/json")]
public class JsonFXCodec : global::OpenRasta.Codecs.IMediaTypeWriter, global::OpenRasta.Codecs.IMediaTypeReader
{
public void WriteTo(object entity, global::OpenRasta.Web.IHttpEntity response, string[] codecParameters)
{
JsonWriter json = new JsonWriter();
using (TextWriter w = new StreamWriter(response.Stream, Encoding.UTF8))
{
json.Write(entity, w);
}
}
public object ReadFrom(global::OpenRasta.Web.IHttpEntity request, global::OpenRasta.TypeSystem.IType destinationType, string destinationName)
{
JsonReader json = new JsonReader();
using (TextReader r = new StreamReader(request.Stream, Encoding.UTF8))
{
return json.Read(r, destinationType.StaticType);
}
}
public object Configuration { get; set; }
}
}
If it is registered for "object" then it seems to work for any class:
ResourceSpace.Has.ResourcesOfType<object>()
.WithoutUri
.TranscodedBy<JsonFXCodec>();