Merge Sparse Data into Dictionary using Json.NET PopulateObject - c#

I would like to load sparse data in JSON format to get a result with missing data filled in with defaults, but my defaults include predefined instances of an extensible set rather than just fixed fields.
For (arbitrary) example,
Types
class Link
{
public string Addr;
public short Port;
public Link() { Addr = "0.0.0.0"; Port = 80; }
public override string ToString() { return Addr + ":" + Port.ToString(); }
}
class Targets
{
public Link Fixed;
public Dictionary<string, Link> Variable;
public Targets()
{
Fixed = new Link() { Addr = "192.168.0.1" };
Variable = new Dictionary<string, Link>
{
["Common"] = new Link() { Addr = "192.168.0.2" }
};
}
public override string ToString()
{
var result = new System.Text.StringBuilder();
result.Append("Fixed").Append('=').Append(Fixed)
.Append(' ');
foreach (var link in Variable)
{
result.Append(link.Key).Append('=').Append(link.Value)
.Append(' ');
}
return result.ToString();
}
}
Usage
var targets = new Targets();
string json = #"{
'Fixed': { 'Port':12345 },
'Variable': {
'Common': { 'Port':12345 }
}
}";
Newtonsoft.Json.JsonConvert.PopulateObject(json, targets);
Console.WriteLine(targets);
Outputs Fixed=192.168.0.1:12345 Common=0.0.0.0:12345 rather than the desired Fixed=192.168.0.1:12345 Common=192.168.0.2:12345.
This shows that the desired merge logic works for fixed properties, but not for items in a Dictionary despite the fact that the Dictionary will otherwise serialize/deserialize just like a type with fixed properties.

Took me a while to figure this out. Json.NET has a dedicated function for merging two JObjects together. Here's your example modified to use this method:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
namespace ConsoleApp3
{
class Link
{
public string Addr;
public short Port;
public Link() { Addr = "0.0.0.0"; Port = 80; }
public override string ToString() { return Addr + ":" + Port.ToString(); }
}
class Targets
{
public Link Fixed;
public Dictionary<string, Link> Variable;
public Targets()
{
Fixed = new Link() { Addr = "192.168.0.1" };
Variable = new Dictionary<string, Link>
{
["Common"] = new Link() { Addr = "192.168.0.2" },
["Common2"] = new Link() { Addr = "192.168.0.25" }
};
}
public override string ToString()
{
var result = new System.Text.StringBuilder();
result.Append("Fixed").Append('=').Append(Fixed)
.Append(' ');
foreach (var link in Variable)
{
if (link.Key != "Variable")
result.Append(link.Key).Append('=').Append(link.Value)
.Append(' ');
}
return result.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var targets = new Targets();
JObject o1 = JObject.Parse( #"{
'Fixed': { 'Port':12345 },
'Variable': {
'Common': { 'Port':12345 }
}
}");
JObject o2 = JObject.FromObject(targets);
o2.Merge(o1, new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
string json = o2.ToString();
Console.WriteLine(json);
JsonConvert.PopulateObject(json, targets);
Console.WriteLine(targets);
Console.ReadKey();
}
}
}
The output is:
{
"Fixed": {
"Addr": "192.168.0.1",
"Port": 12345
},
"Variable": {
"Common": {
"Addr": "192.168.0.2",
"Port": 12345
},
"Common2": {
"Addr": "192.168.0.25",
"Port": 80
}
}
}
Fixed=192.168.0.1:12345 Common=192.168.0.2:12345 Common2=192.168.0.25:80
EDIT by OP: Refined into extension methods without extra ToString/deserialization:
static class SerializerExtensions
{
public static T MergeObject<T>(this JsonSerializer serializer, JsonReader json, T target)
{
JObject o1 = JObject.FromObject(target, serializer);
JObject o2 = serializer.Deserialize(json) as JObject;
o1.Merge(o2, new JsonMergeSettings
{ // union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union,
// an explicit null removes an existing item
MergeNullValueHandling = MergeNullValueHandling.Merge,
});
serializer.Populate(o1.CreateReader(), target);
return target;
}
public static T MergeObject<T>(this JsonSerializer serializer, JsonReader json, JObject template)
{
JObject o1 = template.DeepClone() as JObject;
JObject o2 = serializer.Deserialize(json) as JObject;
o1.Merge(o2, new JsonMergeSettings
{ // union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union,
// an explicit null removes an existing item
MergeNullValueHandling = MergeNullValueHandling.Merge,
});
return serializer.Deserialize<T>(o1.CreateReader());
}
}

Related

Accessing a Json array as string array

I have this code which reads from my json file an array of words
public static string[] GetProfanity()
{
var json = string.Empty;
using (var fs = File.OpenRead("profanity.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = sr.ReadToEnd();
var profanityJson = JsonConvert.DeserializeObject<ProfanityJson>(json);
return profanityJson.badwords;
}
This is the json
{
"badwords" : ["bad", "stupid"]
}
And i try to access this here
public static bool ProfanityCheck(string inputString)
{
string[] badWords = GetProfanity();
string checkString = inputString.ToLower();
if (badWords.Any(checkString.Contains))
return true;
return false;
}
As requested I access the ProfanityCheck method here
[Command("echo")]
[Description("says whatever the user gives")]
public async Task Echo(CommandContext ctx, [RemainingText] string echoText)
{
bool hasProfanity = ProfanityFilter.ProfanityCheck(echoText);
if(hasProfanity)
{
var errMsg = ProfanityFilter.ErrorMessage();
var errSent = await ctx.Channel.SendMessageAsync(embed: errMsg).ConfigureAwait(false);
Thread.Sleep(3000);
await ctx.Channel.DeleteMessageAsync(errSent).ConfigureAwait(false);
await ctx.Channel.DeleteMessageAsync(ctx.Message).ConfigureAwait(false);
return;
}
await ctx.Channel.SendMessageAsync(echoText).ConfigureAwait(false);
}
and the struct I Deserialize it as
public struct ProfanityJson
{
[JsonProperty("badwords")]
public string[] badwords { get; private set; }
}
but when i attempt to search for this any bad words in a string I pass, nothing happens, no errors in the console, no output otherwise. I have it set up so that it sends me an error message when profanity is found, but in its current state it does nothing when profanity is passed
Your code seems to be correct... I would write the GetProfanity() in another way (and I wouldn't surely reread it every time a word is passed to to ProfanityCheck) but this is tangential to your problem. I've written a minimum testable example:
public class ProfanityJson
{
public string[] badwords { get; set; }
}
public static class ProfanityChecker
{
public static string[] GetProfanity()
{
var json = string.Empty;
using (var fs = File.OpenRead("profanity.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = sr.ReadToEnd();
var profanityJson = JsonConvert.DeserializeObject<ProfanityJson>(json);
return profanityJson.badwords;
}
public static string[] GetProfanity2()
{
using (var sr = new StreamReader("profanity.json"))
using (var jtr = new JsonTextReader(sr))
{
var ser = new JsonSerializer();
var profanityJson = ser.Deserialize<ProfanityJson>(jtr);
return profanityJson.badwords;
}
}
public static bool ProfanityCheck(string inputString)
{
string[] badWords = GetProfanity2();
Trace.WriteLine($"Loaded {badWords.Length} bad words");
string checkString = inputString.ToLower();
if (badWords.Any(checkString.Contains))
return true;
return false;
}
}
static void Main(string[] args)
{
Console.WriteLine(ProfanityChecker.ProfanityCheck("badder"));
}
So the only idea I have is that you are using a "stale" version of profanity.json. I've added a little loggin in the ProfanityCheck() method. It will go to the Output pane in Visual Studio.
(Would be a mess as a comment)
You could have your class like this:
public class ProfanityJson
{
[JsonProperty("badwords")]
public string[] Badwords { get; set; }
}
Is it like so? Json is case sensitive.

NewtonSoft json parsing

Can somebody help me to parse the json and get the details.
Lets say i have Top2 input parameter as Police and i want to know the respective Top3 and in that Top3 array i need to check whether Test1Child value is there or not.
I am using newtonsoft json + c# and so far i can get till DeviceTypes using below code
var json = File.ReadAllText(jsonFile); // below json is stored in file jsonFile
var jObject = JObject.Parse(json);
JArray MappingArray =(JArray)jObject["Top1"];
string strTop1 = ObjZone.Top1.ToString();
string strTop2 = ObjZone.Top2.ToString();
var JToken = MappingArray.Where(obj => obj["Top2"].Value<string>() == strTop2).ToList();
//Json
{
"Top1": [
{
"Top2": "Test1",
"Top3": [
"Test1Child"
]
},
{
"Top2": "Test2",
"Top3": [
"Test2Child"
]
}
]
}
I'd use http://json2csharp.com/ (or any other json to c# parser) and then use C# objects (it's just easier for me)
This would look like that for this case:
namespace jsonTests
{
public class DeviceTypeWithResponseTypeMapper
{
public string DeviceType { get; set; }
public List<string> ResponseTypes { get; set; }
}
public class RootObject
{
public List<DeviceTypeWithResponseTypeMapper> DeviceTypeWithResponseTypeMapper { get; set; }
}
}
and the use it like that in code:
var rootob = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(str);
var thoseThatHaveNotUsed = rootob.DeviceTypeWithResponseTypeMapper.Where(dtwrtm =>
dtwrtm.ResponseTypes.Any(rt => rt == "NotUsed"));
foreach (var one in thoseThatHaveNotUsed)
{
Console.WriteLine(one.DeviceType);
}
this code lists all the Device types that have "NotUsed" among the responses.
version 2 (extending your code) would look like that, i believe:
static void Main(string[] args)
{
var json = str; // below json is stored in file jsonFile
var jObject = JObject.Parse(json);
JArray ZoneMappingArray = (JArray)jObject["DeviceTypeWithResponseTypeMapper"];
string strDeviceType = "Police";
string strResponseType = "NotUsed";
var JToken = ZoneMappingArray.Where(obj => obj["DeviceType"].Value<string>() == strDeviceType).ToList();
var isrespTypeThere = JToken[0].Last().Values().Any(x => x.Value<string>() == strResponseType);
Console.WriteLine($"Does {strDeviceType} have response type with value {strResponseType}? {yesorno(isrespTypeThere)}");
}
private static object yesorno(bool isrespTypeThere)
{
if (isrespTypeThere)
{
return "yes!";
}
else
{
return "no :(";
}
}
result:
and if you'd like to list all devices that have response type equal to wanted you can use this code:
var allWithResponseType = ZoneMappingArray.Where(jt => jt.Last().Values().Any(x => x.Value<string>() == strResponseType));
foreach (var item in allWithResponseType)
{
Console.WriteLine(item["DeviceType"].Value<string>());
}

json properties to lower case c#

I'm getting from client json string:
{ "Client": { "Name": "John" } }
but for the further handling I need the following json:
{ "client": { "name": "John" } }
I tried something like that, but it didn't help:
public class LowerCaseNamingStrategy : NamingStrategy
{
protected override string ResolvePropertyName(string name)
{
return name.ToLower();
}
}
and
var settings = new JsonSerializerSettings();
settings.ContractResolver = new DefaultContractResolver { NamingStrategy = new LowerCaseNamingStrategy() };
var json = JsonConvert.DeserializeObject(input.DataJson, settings);
JSON is dynamic object, so I don't know properties are there.
How can I do that with c#? With using Newtonsoft.Json or may be with using Xml.
If I understood you correctly, you need to modify properties in your Json string, but not convert the Json into object.
In this case you can try to parse Json into JObject and replace properties in that object.
private static void ChangePropertiesToLowerCase(JObject jsonObject)
{
foreach (var property in jsonObject.Properties().ToList())
{
if(property.Value.Type == JTokenType.Object)// replace property names in child object
ChangePropertiesToLowerCase((JObject)property.Value);
property.Replace(new JProperty(property.Name.ToLower(),property.Value));// properties are read-only, so we have to replace them
}
}
sample:
var jsonString = #"{ ""Client"": { ""Name"": ""John"" } }";
var jobj = JObject.Parse(jsonString, new JsonLoadSettings());
ChangePropertiesToLowerCase(jobj);
var stringWithLowerCaseProperties = jobj.ToString(Formatting.None);
Try LowercaseContractResolver instead
var settings = new JsonSerializerSettings();
settings.ContractResolver = new LowercaseContractResolver();
var json = JsonConvert.DeserializeObject(input.DataJson, settings);
Extending Anton Semenov answer for cases when JSON can contain an Array of Objects:
private static void ChangePropertiesToLowerCase(JObject jsonObject)
{
foreach (var property in jsonObject.Properties().ToList())
{
if (property.Value.Type == JTokenType.Object) // replace property names in child object
ChangePropertiesToLowerCase((JObject)property.Value);
if (property.Value.Type == JTokenType.Array)
{
var arr = JArray.Parse(property.Value.ToString());
foreach (var pr in arr)
{
ChangePropertiesToLowerCase((JObject)pr);
}
property.Value = arr;
}
property.Replace(new JProperty(property.Name.ToLower(CultureInfo.InvariantCulture), property.Value)); // properties are read-only, so we have to replace them
}
}
All other solutions here modify the original object. Here's an immutable version which returns a new object with lowercase properties:
public static class JsonExtensions
{
public static JToken ToLowerRecursive(this JToken token)
{
if (token is JObject jobj)
return new JObject(jobj.Properties().Select(x => new JProperty(x.Name.ToLowerInvariant(), x.Value.ToLowerRecursive())));
if (token is JArray jarr)
return new JArray(jarr.Select(x => x.ToLowerRecursive()));
return token;
}
}
This is easy way without Regex. Replace every [{ "A] with [{ "a]
var json = "{ \"Client\": { \"Name\": \"John\" } }";
var newJson = string.Empty;
foreach (var w in json.Split(new[] { "{ \"" }, StringSplitOptions.RemoveEmptyEntries))
{
if (w[0] != null)
{
newJson += "{ \"" + (w[0].ToString().ToLower()) + w.Remove(0,1);
}
}
result:
"{ \"client\": { \"name\": \"John\" } }"

Serialise entire Page tree to JSON in EpiServer

I am completely new to EpiServer and this has been killing me for days :(
I am looking for a simple way to convert a page and all it's descendents to a JSON tree.
I have got this far:
public class MyPageController : PageController<MyPage>
{
public string Index(MyPage currentPage)
{
var output = new ExpandoObject();
var outputDict = output as IDictionary<string, object>;
var pageRouteHelper = ServiceLocator.Current.GetInstance<EPiServer.Web.Routing.PageRouteHelper>();
var pageReference = pageRouteHelper.PageLink;
var children = DataFactory.Instance.GetChildren(pageReference);
var toOutput = new { };
foreach (PageData page in children)
{
outputDict[page.PageName] = GetAllContentProperties(page, new Dictionary<string, object>());
}
return outputDict.ToJson();
}
public Dictionary<string, object> GetAllContentProperties(IContentData content, Dictionary<string, object> result)
{
foreach (var prop in content.Property)
{
if (prop.IsMetaData) continue;
if (prop.GetType().IsGenericType &&
prop.GetType().GetGenericTypeDefinition() == typeof(PropertyBlock<>))
{
var newStruct = new Dictionary<string, object>();
result.Add(prop.Name, newStruct);
GetAllContentProperties((IContentData)prop, newStruct);
continue;
}
if (prop.Value != null)
result.Add(prop.Name, prop.Value.ToString());
}
return result;
}
}
The problem is, by converting the page structure to Dictionaries, the JsonProperty PropertyName annotations in my pages are lost:
[ContentType(DisplayName = "MySubPage", GroupName = "MNRB", GUID = "dfa8fae6-c35d-4d42-b170-cae3489b9096", Description = "A sub page.")]
public class MySubPage : PageData
{
[Display(Order = 1, Name = "Prop 1")]
[CultureSpecific]
[JsonProperty(PropertyName = "value-1")]
public virtual string Prop1 { get; set; }
[Display(Order = 2, Name = "Prop 2")]
[CultureSpecific]
[JsonProperty(PropertyName = "value-2")]
public virtual string Prop2 { get; set; }
}
This means I get JSON like this:
{
"MyPage": {
"MySubPage": {
"prop1": "...",
"prop2": "..."
}
}
}
Instead of this:
{
"MyPage": {
"MySubPage": {
"value-1": "...",
"value-2": "..."
}
}
}
I know about using custom ContractResolvers for the JSON serialisation, but that will not help me because I need JSON property names that cannot be inferred from the C# property name.
I would also like to be able to set custom JSON property names for the pages themselves.
I really hope that a friendly EpiServer guru can help me out here!
Thanks in advance :)
One of the C# dev's on my project rolled his own solution for this in the end. He used reflection to examine the page tree and built the JSON up from that. Here it is. Hopefully it will help someone else as much as it did me!
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAnnotations;
using EPiServer.ServiceLocation;
using EPiServer.Web.Mvc;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
using System;
using System.Runtime.Caching;
using System.Linq;
using Newtonsoft.Json.Linq;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
namespace NUON.Models.MyCorp
{
public class MyCorpPageController : PageController<MyCorpPage>
{
public string Index(MyCorpPage currentPage)
{
Response.ContentType = "text/json";
// check if the JSON is cached - if so, return it
ObjectCache cache = MemoryCache.Default;
string cachedJSON = cache["myCorpPageJson"] as string;
if (cachedJSON != null)
{
return cachedJSON;
}
var output = new ExpandoObject();
var outputDict = output as IDictionary<string, object>;
var pageRouteHelper = ServiceLocator.Current.GetInstance<EPiServer.Web.Routing.PageRouteHelper>();
var pageReference = pageRouteHelper.PageLink;
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
var children = contentLoader.GetChildren<PageData>(currentPage.PageLink).OfType<PageData>();
var toOutput = new { };
var jsonResultObject = new JObject();
foreach (PageData page in children)
{
// Name = e.g. BbpbannerProxy . So remove "Proxy" and add the namespace
var classType = Type.GetType("NUON.Models.MyCorp." + page.GetType().Name.Replace("Proxy", string.Empty));
// Only keep the properties from this class, not the inherited properties
jsonResultObject.Add(page.PageName, GetJsonObjectFromType(classType, page));
}
// add to cache
CacheItemPolicy policy = new CacheItemPolicy();
// expire the cache daily although it will be cleared whenever content changes.
policy.AbsoluteExpiration = DateTimeOffset.Now.AddDays(1.0);
cache.Set("myCorpPageJson", jsonResultObject.ToString(), policy);
return jsonResultObject.ToString();
}
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule),
typeof(EPiServer.Web.InitializationModule))]
public class EventsInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
var events = ServiceLocator.Current.GetInstance<IContentEvents>();
events.PublishedContent += PublishedContent;
}
public void Preload(string[] parameters)
{
}
public void Uninitialize(InitializationEngine context)
{
}
private void PublishedContent(object sender, ContentEventArgs e)
{
// Clear the cache because some content has been updated
ObjectCache cache = MemoryCache.Default;
cache.Remove("myCorpPageJson");
}
}
private static JObject GetJsonObjectFromType(Type classType, object obj)
{
var jsonObject = new JObject();
var properties = classType.GetProperties(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.DeclaredOnly);
foreach (var property in properties)
{
var jsonAttribute = property.GetCustomAttributes(true).FirstOrDefault(a => a is JsonPropertyAttribute);
var propertyName = jsonAttribute == null ? property.Name : ((JsonPropertyAttribute)jsonAttribute).PropertyName;
if (property.PropertyType.BaseType == typeof(BlockData))
jsonObject.Add(propertyName, GetJsonObjectFromType(property.PropertyType, property.GetValue(obj)));
else
{
var propertyValue = property.PropertyType == typeof(XhtmlString) ? property.GetValue(obj)?.ToString() : property.GetValue(obj);
if (property.PropertyType == typeof(string))
{
propertyValue = propertyValue ?? String.Empty;
}
jsonObject.Add(new JProperty(propertyName, propertyValue));
}
}
return jsonObject;
}
}
[ContentType(DisplayName = "MyCorpPage", GroupName = "MyCorp", GUID = "bc91ed7f-d0bf-4281-922d-1c5246cab137", Description = "The main MyCorp page")]
public class MyCorpPage : PageData
{
}
}
Hi I'm looking for the same thing, and until now I find this page and component.
https://josefottosson.se/episerver-contentdata-to-json/
https://github.com/joseftw/JOS.ContentJson
I hope you find it useful

.NET binary XML with pre-shared dictionary

I'm using XmlDictionaryWriter to serialize objects to a database with data contract serializer.
It works great, both size and speed are 2 times better then using text/xml.
However, I'll have to deal with enormous count of records in my database, where any extra bytes are directly translated into the gigabytes of the DB size.
That's why I'd love to reduce the size further, by using an XML dictionary.
How do I do that?
I see that XmlDictionaryWriter.CreateBinaryWriter static method accepts the 2-nd parameter of type IXmlDictionary. The MSDN says "The XmlDictionary to use as the shared dictionary".
First I've tried to use the system-supplied implementation:
XmlDictionary dict = new XmlDictionary();
string[] dictEntries = new string[]
{
"http://schemas.datacontract.org/2004/07/MyContracts",
"http://www.w3.org/2001/XMLSchema-instance",
"MyElementName1",
"MyElementName2",
"MyElementName3",
};
foreach ( string s in dictEntries )
dict.Add( s );
The result is .NET framework completely ignores the dictionary, and still inserts the above strings as plain text instead of just referencing a corresponding dictionary entry.
Then I've created my own implementation of IXmlDictionary:
class MyDictionary : IXmlDictionary
{
Dictionary<int, string> values = new Dictionary<int, string>();
Dictionary<string, int> keys = new Dictionary<string, int>();
MyDictionary()
{
string[] dictEntries = new string[]
{
"http://schemas.datacontract.org/2004/07/MyContracts",
"http://www.w3.org/2001/XMLSchema-instance",
"MyElementName1",
"MyElementName2",
"MyElementName3",
};
foreach ( var s in dictEntries )
this.Add( s );
}
static IXmlDictionary s_instance = new MyDictionary();
public static IXmlDictionary instance { get { return s_instance; } }
void Add( string val )
{
if ( keys.ContainsKey( val ) )
return;
int id = values.Count + 1;
values.Add( id, val );
keys.Add( val, id );
}
bool IXmlDictionary.TryLookup( XmlDictionaryString value, out XmlDictionaryString result )
{
if ( value.Dictionary == this )
{
result = value;
return true;
}
return this.TryLookup( value.Value, out result );
}
bool IXmlDictionary.TryLookup( int key, out XmlDictionaryString result )
{
string res;
if ( !values.TryGetValue( key, out res ) )
{
result = null;
return false;
}
result = new XmlDictionaryString( this, res, key );
return true;
}
public bool /* IXmlDictionary. */ TryLookup( string value, out XmlDictionaryString result )
{
int key;
if ( !keys.TryGetValue( value, out key ) )
{
result = null;
return false;
}
result = new XmlDictionaryString( this, value, key );
return true;
}
}
The result is - my TryLookup methods are called OK, however DataContractSerializer.WriteObject produces an empty document.
How do I use a pre-shared dictionary?
Thanks in advance!
P.S. I don't want to mess with XmlBinaryReaderSession/XmlBinaryWriterSession: I don't have "sessions", instead I have a 10 GB+ database accessed by many threads at once. What I want is just static pre-defined dictionary.
Update: OK I've figured out that I just need to call "XmlDictionaryWriter.Flush". The only remaining question is - why doesn't the system-supplied IXmlDictionary implementation work as expected?
for the XmlDictionaryWriter you need to use session.
example:
private static Stream SerializeBinaryWithDictionary(Person person,DataContractSerializer serializer)
{
var stream = new MemoryStream();
var dictionary = new XmlDictionary();
var session = new XmlBinaryWriterSession();
var key = 0;
session.TryAdd(dictionary.Add("FirstName"), out key);
session.TryAdd(dictionary.Add("LastName"), out key);
session.TryAdd(dictionary.Add("Birthday"), out key);
session.TryAdd(dictionary.Add("Person"), out key);
session.TryAdd(dictionary.Add("http://www.friseton.com/Name/2010/06"),out key);
session.TryAdd(dictionary.Add("http://www.w3.org/2001/XMLSchema-instance"),out key);
var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session);
serializer.WriteObject(writer, person);
writer.Flush();
return stream;
}
The only way I way able to replicate the issue with the IXmlDictionary not being used was when my class wasn't decorated with a DataContract attribute. The following app displays the difference in sizes with decorated and undecorated classes.
using System;
using System.Runtime.Serialization;
using System.Xml;
namespace XmlPresharedDictionary
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Serialized sizes");
Console.WriteLine("-------------------------");
TestSerialization<MyXmlClassUndecorated>("Undecorated: ");
TestSerialization<MyXmlClassDecorated>("Decorated: ");
Console.ReadLine();
}
private static void TestSerialization<T>(string lineComment) where T : new()
{
XmlDictionary xmlDict = new XmlDictionary();
xmlDict.Add("MyElementName1");
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, xmlDict))
{
serializer.WriteObject(writer, new T());
writer.Flush();
Console.WriteLine(lineComment + stream.Length.ToString());
}
}
}
//[DataContract]
public class MyXmlClassUndecorated
{
public MyElementName1[] MyElementName1 { get; set; }
public MyXmlClassUndecorated()
{
MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") };
}
}
[DataContract]
public class MyXmlClassDecorated
{
public MyElementName1[] MyElementName1 { get; set; }
public MyXmlClassDecorated()
{
MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") };
}
}
[DataContract]
public class MyElementName1
{
[DataMember]
public string Value { get; set; }
public MyElementName1(string value) { Value = value; }
}
}

Categories

Resources