Implementation of an IText's IExternalSignatureContainer with BouncyCastle to manage ECDSA-SHA384 signature - cryptography

I am trying to implement an IExternalSignatureContainer (itext7) to sign PDF with belgian id card.
There is twa smartcard versions to deal with:
RSA/SHA256 signature
ECDSA/SHA384
I achieved to sign the pdf with the RSA/SHA256 siganture but I am not able to make the same for the ECDSA/SHA384.
Here is the Sign method of the ExternalSignatureContainer:
public byte[] Sign(Stream data)
{
var signCert = this.chain.First();
IssuerAndSerialNumber issuerAndSerialNumber = new IssuerAndSerialNumber(signCert.IssuerDN, signCert.SerialNumber);
var signerGenerator = new SignerInfoGeneratorBuilder().Build(
new BeidSignatureFactory(
(data) => this.Sign(data), // Sign data with the eid-middleware
() => this.GetHashAlgorithm(), // Get the hash algorithm of the id card from the eid-middleware
() => this.GetEncryptionAlgorithm() // Get the encryption algorithm of the id card from the eid-middleware
), signCert);
CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
gen.AddSignerInfoGenerator(signerGenerator);
IX509Store x509CertStore = X509StoreFactory.Create(
"Certificate/Collection",
new X509CollectionStoreParameters(this.chain));
gen.AddCertificates(x509CertStore);
CmsProcessableInputStream cmsProcessableInputStream = new CmsProcessableInputStream(data);
var sigData = gen.Generate(cmsProcessableInputStream, false);
return sigData.GetEncoded();
}
Here is a Custom ISignatureFactory and a custom ISigner that use the eid-middleware to access the begian eid card and use it to sign data.
internal class BeidSignatureFactory : ISignatureFactory
{
private readonly BeidSigner _signer = new BeidSigner();
private readonly BeidSigner.SignatureGenerator _signatureGenerator;
private readonly BeidSigner.AlgorithmGetter _getHashAlgorithm;
private readonly BeidSigner.AlgorithmGetter _getEncryptionAlgorithm;
public BeidSignatureFactory(BeidSigner.SignatureGenerator signatureGenerator, BeidSigner.AlgorithmGetter getHashAlgorithm, BeidSigner.AlgorithmGetter getEncryptionAlgorithm)
{
this._signatureGenerator = signatureGenerator;
this._getEncryptionAlgorithm = getEncryptionAlgorithm;
this._getHashAlgorithm = getHashAlgorithm;
}
public IStreamCalculator CreateCalculator()
{
var signer = new BeidSigner
{
Sign = this._signatureGenerator,
GetEncryptionAlgorithm = this._getEncryptionAlgorithm,
GetHashAlgorithm = this._getHashAlgorithm,
};
signer.Init(true, null);
return new DefaultSignatureCalculator(signer);
}
public object AlgorithmDetails
{
get
{
string encryptionAlgorithm = this._getEncryptionAlgorithm();
string hashAlgorithm = this._getHashAlgorithm();
switch (encryptionAlgorithm)
{
case "RSA":
return new AlgorithmIdentifier(PkcsObjectIdentifiers.Sha256WithRsaEncryption);
case "ECDSA":
return new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha384);
default:
return null;
}
}
}
}
internal class BeidSigner : ISigner
{
public delegate byte[] SignatureGenerator(byte[] data);
public delegate string AlgorithmGetter();
private byte[] _input;
public void Init(bool forSigning, ICipherParameters parameters)
{
this.Reset();
}
public void Update(byte input)
{
throw new NotImplementedException();
}
public void BlockUpdate(byte[] input, int inOff, int length)
{
this._input = input.Skip(inOff).Take(length).ToArray();
}
public byte[] GenerateSignature()
{
// This method doesn't exist, you will need to implement your own method here
return this.Sign(this._input);
}
public bool VerifySignature(byte[] signature)
{
throw new NotImplementedException();
}
public void Reset() { }
public string AlgorithmName
{
get
{
return $"{this.GetHashAlgorithm()}with{this.GetEncryptionAlgorithm()}";
}
}
public SignatureGenerator Sign { get; set; }
public AlgorithmGetter GetHashAlgorithm { get; set; }
public AlgorithmGetter GetEncryptionAlgorithm { get; set; }
}
Once the pdf signed with the EDCSA signature, the signature is not validated by acrobat reader "Document altered ro corrupted".
Perhaps I have completaly misunderstood the way tho sign the pdf.
Thanks for your help.

Related

How do you write data into a Redis custom state store using Kafka Streams

I've recently been learning about how to use the Kafka Streams client and one thing that I've been struggling with is how to switch from the default state store (RocksDB) to a custom state store using something like Redis. The Confluent documentation makes it clear you have to implement the StateStore interface for your custom store and you must provide an implementation of StoreBuilder for creating instances of that store.
Here's what I have so far for my custom store. I've also added a simple write method to append a new entry into a specified stream via the Redis XADD command.
public class MyCustomStore<K,V> implements StateStore, MyWriteableCustomStore<K,V> {
private String name;
private volatile boolean open = false;
private boolean loggingEnabled = false;
public MyCustomStore(String name, boolean loggingEnabled) {
this.name = name;
this.loggingEnabled = loggingEnabled;
}
#Override
public String name() {
return this.name;
}
#Override
public void init(ProcessorContext context, StateStore root) {
if (root != null) {
// register the store
context.register(root, (key, value) -> {
write(key.toString(), value.toString());
});
}
this.open = true;
}
#Override
public void flush() {
// TODO Auto-generated method stub
}
#Override
public void close() {
// TODO Auto-generated method stub
}
#Override
public boolean persistent() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isOpen() {
// TODO Auto-generated method stub
return false;
}
#Override
public void write(String key, String value) {
try(Jedis jedis = new Jedis("localhost", 6379)) {
Map<String, String> hash = new HashMap<>();
hash.put(key, value);
jedis.xadd("MyStream", StreamEntryID.NEW_ENTRY, hash);
}
}
}
public class MyCustomStoreBuilder implements StoreBuilder<MyCustomStore<String,String>> {
private boolean cached = true;
private String name;
private Map<String,String> logConfig=new HashMap<>();
private boolean loggingEnabled;
public MyCustomStoreBuilder(String name, boolean loggingEnabled){
this.name = name;
this.loggingEnabled = loggingEnabled;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withCachingEnabled() {
this.cached = true;
return this;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withCachingDisabled() {
this.cached = false;
return null;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withLoggingEnabled(Map<String, String> config) {
loggingEnabled=true;
return this;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withLoggingDisabled() {
this.loggingEnabled = false;
return this;
}
#Override
public MyCustomStore<String,String> build() {
return new MyCustomStore<String,String>(this.name, this.loggingEnabled);
}
#Override
public Map<String, String> logConfig() {
return logConfig;
}
#Override
public boolean loggingEnabled() {
return loggingEnabled;
}
#Override
public String name() {
return name;
}
}
And here's what my setup and topology look like.
#Bean
public KafkaStreams kafkaStreams(KafkaProperties kafkaProperties) {
final Properties props = new Properties();
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
props.put(StreamsConfig.APPLICATION_ID_CONFIG, appName);
props.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl);
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Long().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Double().getClass());
props.put(StreamsConfig.STATE_DIR_CONFIG, "data");
props.put(StreamsConfig.APPLICATION_SERVER_CONFIG, appServerConfig);
props.put(JsonDeserializer.VALUE_DEFAULT_TYPE, JsonNode.class);
props.put(DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG, LogAndContinueExceptionHandler.class);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
final String storeName = "the-custome-store";
Topology topology = new Topology();
// Create CustomStoreSupplier for store name the-custom-store
MyCustomStoreBuilder customStoreBuilder = new MyCustomStoreBuilder(storeName, false);
topology.addSource("input","inputTopic");
topology.addProcessor("redis-processor", () -> new RedisProcessor(storeName), "input");
topology.addStateStore(customStoreBuilder, "redis-processor");
KafkaStreams kafkaStreams = new KafkaStreams(topology, props);
kafkaStreams.start();
return kafkaStreams;
}
public class MyCustomStoreType<K,V> implements QueryableStoreType<MyReadableCustomStore<String,String>> {
#Override
public boolean accepts(StateStore stateStore) {
return stateStore instanceof MyCustomStore;
}
#Override
public MyReadableCustomStore<String,String> create(final StateStoreProvider storeProvider, final String storeName) {
return new MyCustomStoreTypeWrapper<>(storeProvider, storeName, this);
}
}
public class MyCustomStoreTypeWrapper<K,V> implements MyReadableCustomStore<K,V> {
private final QueryableStoreType<MyReadableCustomStore<String, String>> customStoreType;
private final String storeName;
private final StateStoreProvider provider;
public MyCustomStoreTypeWrapper(final StateStoreProvider provider,
final String storeName,
final QueryableStoreType<MyReadableCustomStore<String, String>> customStoreType) {
this.provider = provider;
this.storeName = storeName;
this.customStoreType = customStoreType;
}
#Override
public String read(String key) {
try (Jedis jedis = new Jedis("localhost", 6379)) {
StreamEntryID start = new StreamEntryID(0, 0);
StreamEntryID end = null; // null -> until the last item in the stream
int count = 2;
List<StreamEntry> list = jedis.xrange("MyStream", start, end, count);
if (list != null) {
// Get the most recently added item, which is also the last item
StreamEntry streamData = list.get(list.size() - 1);
return streamData.toString();
} else {
System.out.println("No new data in the stream");
}
return "";
}
}
}
// This throws the InvalidStateStoreException when I try to get access to the custom store
MyReadableCustomStore<String,String> store = streams.store("the-custome-store", new MyCustomStoreType<String,String>());
String value = store.read("testKey");
So, my question is how do I actually get the state store data to persist into Redis now? I feel like I'm missing something in the state store initialization or with the StateRestoreCallback. Any help or clarification with this would be greatly appreciated.
It looks to me that you have the store wired up to the topology correctly. But you don't have any processors using the store.
It could look something like this:
final String storeName = "the-custome-store";
MyCustomStoreBuilder customStoreBuilder = new MyCustomStoreBuilder(storeName, false);
Topology topology = new Topology()
topology.addSource("input", "input-topic");
// makes the processor a child of the source node
// the source node forwards its records to the child processor node
topology.addProcessor("redis-processor", () -> new RedisProcessor(storeName), "input");
// add the store and specify the processor(s) that access the store
topology.addStateStore(storeBuilder, "redis-processor");
class RedisProcessor implements Processor<byte[], byte[]> {
final String storeName;
MyCustomStore<byte[],byte[]> stateStore;
public RedisProcessor(String storeName) {
this.storeName = storeName;
}
#Override
public void init(ProcessorContext context) {
stateStore = (MyCustomeStore<byte[], byte[]>) context.getStateStore(storeName);
}
#Override
public void process(byte[] key, byte[] value) {
stateStore.write(key, value);
}
#Override
public void close() {
}
}
HTH, and let me know how it works out for you.
Update to answer from comments:
I think you need to update MyCustomStore.isOpen() to return the open variable.
Right now it's hardcoded to return false
Override
public boolean isOpen() {
// TODO Auto-generated method stub
return false;
}

.Net Core - Using Authorization filter to check a variable from request and modify it

In below code, I need to read value of variable from request and modify request object.
public class ApiAuthFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// 1. read request object from context
// 2. modify the value
// 3. add the value and update request
}
}
You could modify request object like below:
1.User:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
2.ApiAuthFilter:
public class ApiAuthFilter : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
//1.read request object from context
var request = context.HttpContext.Request;
request.EnableRewind();
request.Body.Position = 0;
using (var reader = new StreamReader(request.Body))
{
//2.modify the value
var decriptedFromJavascript = "{ \"Id\":2,\"Name\":\"UR123456\"}";
byte[] bytes = Encoding.ASCII.GetBytes(decriptedFromJavascript);
//3. add the value and update request
request.Body = new MemoryStream(bytes);
}
}
}
3.Action:
[HttpPost("[action]")]
[ApiAuthFilter]
public User GetUser([FromBody]User user)
{
return user;
}
4.Result:
public void OnAuthorization(AuthorizationFilterContext context)
{
string authHeader = context.HttpContext.Request.Headers["Authorization"];
context.HttpContext.Request.Headers["Authorization"] = "value";
}

How to keep user logged in after browser is closed

Every time I close the browser I need to log in again into this app. It is developed in .NET Core 2.0. I'm trying to let it logged in, like every other regular site.
I checked this post that may be useful, but since the code is quite different from this application I decided to create this post.
This is my security code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
namespace Petito.Common
{
public interface IActivityContext
{
string ActivityID { get; }
IPrincipal User { get; }
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityIdentity : IIdentity
{
private string _name = null;
[JsonIgnore()]
private bool _isAuthenticated = false;
[JsonIgnore()]
private string _authenticationType = "";
public ActivityIdentity()
{
}
public ActivityIdentity(string name) : this(name, false, "")
{
}
internal ActivityIdentity(string name, bool isAuthenticated, string authenticationType)
{
this._name = name;
this._isAuthenticated = isAuthenticated;
this._authenticationType = authenticationType;
}
public string Name { get => _name; }
public bool IsAuthenticated { get => _isAuthenticated; }
public string AuthenticationType { get => _authenticationType; }
public static ActivityIdentity Unathenticated => new ActivityIdentity();
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityPrincipal : IPrincipal
{
private ActivityIdentity _activityIdentity = null;
private string[] _roles = null;
public ActivityPrincipal() : this(ActivityIdentity.Unathenticated, null)
{
}
public ActivityPrincipal(ActivityIdentity activityIdentity, params string[] roles)
{
_activityIdentity = activityIdentity;
_roles = roles;
}
public ActivityIdentity Identity => _activityIdentity;
IIdentity IPrincipal.Identity => _activityIdentity;
public bool IsInRole(string role)
{
if (_roles != null && _roles.Length > 0)
{
return _roles.Contains(role);
}
return false;
}
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityContext : IDisposable, IActivityContext
{
private string _activityID = Guid.NewGuid().ToString();
private DateTime _startDate = DateTime.UtcNow;
private DateTime? _endDate = null;
private ActivityPrincipal _activityPrincipal = null;
public ActivityContext() : this(null)
{
}
public ActivityContext(IPrincipal principal)
{
_activityPrincipal = Convert(principal);
}
private ActivityPrincipal Convert(IPrincipal principal)
{
if (principal == null)
{
return new ActivityPrincipal();
}
var activityPrincipal = principal as ActivityPrincipal;
if (activityPrincipal != null)
{
return activityPrincipal;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal != null)
{
var roles = claimsPrincipal.Claims.Select(x => x.Value);
var p = new ActivityPrincipal(
new ActivityIdentity(claimsPrincipal.Identity.Name, claimsPrincipal.Identity.IsAuthenticated, claimsPrincipal.Identity.AuthenticationType)
, roles.ToArray()
);
return p;
}
throw new NotSupportedException($"Converting {principal.GetType()} not supported");
}
public void Dispose()
{
if (!_endDate.HasValue)
{
_endDate = DateTime.UtcNow;
}
}
public string ActivityID { get => _activityID; }
public DateTime StartDate { get => _startDate; }
public DateTime? EndDate { get => _endDate; }
public IPrincipal User
{
get
{
return _activityPrincipal;
}
}
}
}
Of course, I'll still try to figure out what's wrong with the code. Please if you need another part of the code other from that I posted let me know.
Thanks!

Xamarin SQLite database creating for all tables

I have working on this topic for 4 hours but I couldn't get any solution.
My problem is actually;
I have 5 table and I wanna create one controller to create different tables.
My current codes are below but this codes create only one table.
public interface ISQLite
{
SQLiteConnection GetConnection();
}
-
public class TodoItem
{
public TodoItem ()
{
}
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public bool Done { get; set; }
}
-
public class TodoItemDatabase
{
static object locker = new object ();
SQLiteConnection database;
/// <summary>
/// Initializes a new instance of the <see cref="Tasky.DL.TaskDatabase"/> TaskDatabase.
/// if the database doesn't exist, it will create the database and all the tables.
/// </summary>
/// <param name='path'>
/// Path.
/// </param>
public TodoItemDatabase()
{
database = DependencyService.Get<ISQLite> ().GetConnection ();
// create the tables
database.CreateTable<TodoItem>();
}
public IEnumerable<TodoItem> GetItems ()
{
lock (locker) {
return (from i in database.Table<TodoItem>() select i).ToList();
}
}
public IEnumerable<TodoItem> GetItemsNotDone ()
{
lock (locker) {
return database.Query<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
}
}
public TodoItem GetItem (int id)
{
lock (locker) {
return database.Table<TodoItem>().FirstOrDefault(x => x.ID == id);
}
}
public int SaveItem (TodoItem item)
{
lock (locker) {
if (item.ID != 0) {
database.Update(item);
return item.ID;
} else {
return database.Insert(item);
}
}
}
public int DeleteItem(int id)
{
lock (locker) {
return database.Delete<TodoItem>(id);
}
}
}
-
public class SQLite_Android : ISQLite
{
public SQLite_Android()
{
}
#region ISQLite implementation
public SQLite.SQLiteConnection GetConnection()
{
var sqliteFilename = "TodoSQLite.db3";
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); // Documents folder
var path = Path.Combine(documentsPath, sqliteFilename);
// This is where we copy in the prepopulated database
Console.WriteLine(path);
if (!File.Exists(path))
{
var s = Forms.Context.Resources.OpenRawResource(Resource.Raw.TodoSQLite); // RESOURCE NAME ###
// create a write stream
FileStream writeStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
// write to the stream
ReadWriteStream(s, writeStream);
}
var conn = new SQLite.SQLiteConnection(path);
// Return the database connection
return conn;
}
#endregion
/// <summary>
/// helper method to get the database out of /raw/ and into the user filesystem
/// </summary>
void ReadWriteStream(Stream readStream, Stream writeStream)
{
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
}
--- How can I create multi tables in one controller ?
Looks like you are using Sqlite.net-pcl, right?
Multiple tables from the same model are not supported (it's for simple cases only).
You can create multiple models (possibly by just inheriting) and then call CreatTable<T> for each of them.
I solved problem. Maybe this solution helps somenone.
I have two DbHepler Class and two model class for creating two tables on DB.
Base connection codes are same;
public interface ISQLite
{
SQLiteConnection GetConnection();
}
This is the App.cs file;
public class App : Application {
public App()
{
authenticationDB = new AuthenticationDbHelper(Database);
settingsDbHelper = new SettingsDbHelper(Database);
MainPage = new Views.MainMenuPage();
}
public static CreateDB Database
{
get
{
if (database == null)
{
database = new CreateDB();
}
return database;
}
}
}
The CreateDB class is necessary for create one db for all tables
public class CreateDB
{
public SQLiteConnection database;
public object locker = new object();
public CreateDB()
{
database = DependencyService.Get<ISQLite>().GetConnection();
}
}
This interface is necessary for created tables actions. Since implement this class we can use theese methods all tables.(T is table class)(To understand look AuthenticationDBHelper class)
public interface SQLiteBase<T>
{
IEnumerable<T> GetItems();
T GetItem(long id);
long SaveItem(T item);
void UpdateItem(T item);
int DeleteItem(int id);
int Clear();
int getCount();
}
This DbHelper class will be used for delete,insert,clear.... items.
public class AuthenticationDbHelper : SQLiteBase<AuthenticationDbTable>
{
SQLiteConnection database;
object locker;
public AuthenticationDbHelper(CreateDB db)
{
database = db.database;
locker = db.locker;
database.CreateTable<AuthenticationDbTable>();
}
public int Clear()
{
lock(locker)
{
return database.DeleteAll<AuthenticationDbTable>();
}
}
public int DeleteItem(int id)
{
lock (locker)
{
return database.Delete<AuthenticationDbTable>(id);
}
}
public AuthenticationDbTable GetItem(long id)
{
lock (locker)
{
return database.Table<AuthenticationDbTable>().FirstOrDefault(x => x.UserId == id);
}
}
public IEnumerable<AuthenticationDbTable> GetItems()
{
lock (locker)
{
return (from i in database.Table<AuthenticationDbTable>() select i).ToList();
}
}
public long SaveItem(AuthenticationDbTable item)
{
lock (locker)
{
return database.Insert(item);
}
}
public void UpdateItem(AuthenticationDbTable item)
{
lock(locker)
{
database.Update(item);
}
}
public int getCount()
{
return GetItems().Count();
}
}
I know it is very confused but this is the last. We will create model for authentication.
public class AuthenticationDbTable
{
public AuthenticationDbTable(long userId, string sessionId, string username, string clientuuid)
{
this.userId = userId;
this.sessionId = sessionId;
this.username = username;
this.clientuuid = clientuuid;
}
private long userId;
private string sessionId;
private string username;
private string clientuuid;
[PrimaryKey]
public long UserId
{
get { return userId; }
set { userId = value; }
}
public string SessionId
{
get { return sessionId; }
set { sessionId = value; }
}
public string Username
{
get { return username; }
set { username = value; }
}
public string Clientuuid
{
get { return clientuuid; }
set { clientuuid = value; }
}
}
Using
AuthenticationDbTable authentication = new AuthenticationDbTable(authenticateduser.User.UserId, r.Retval.SessionStatus.SessionId, authenticateduser.User.Name, authenticateduser.Clientuuid);
App.authenticationDB.SaveItem(authentiaction);
Note
For creating second table you can use same way. You should create second DbHelper and model class. Assume that you will create a table for settings. You should create SettingsDbHelper and SettingsDbTable class. through same way.
Thank you :)

Serializing object graph using MongoDB Bson serializer

I've been playing a little with the MongoDB Bson serializer, using the following piece of code:
class Program
{
public class myValue
{
public int Id = 0;
public string Label = "";
}
public class myValueMap : Dictionary<string, myValue>
{
}
public class myProdData
{
public myValueMap Mapping { get; set; }
}
public class mySystemPosition
{
public string Text { get; set; }
public myProdData ProdData { get; set; }
}
static void Main(string[] args)
{
BsonClassMap.RegisterClassMap<mySystemPosition>();
BsonClassMap.RegisterClassMap<myProdData>();
BsonClassMap.RegisterClassMap<myValueMap>();
BsonClassMap.RegisterClassMap<myValue>();
var o = new mySystemPosition()
{
ProdData = new myProdData()
{
Mapping = new myValueMap()
{
{"123", new myValue() {Id = 1, Label = "Item1"}},
{"345", new myValue() {Id = 2, Label = "Item2"}},
}
}
};
var bson = o.ToBson();
var text = Encoding.ASCII.GetString(bson);
}
}
however I don't seem to be able to get the myProdData.Mapping serialized....
Do I need to configure the MongoDB Bson serializer in a special way, to make this work?
You no need to use BsonClassMap.RegisterClassMap if you no need custom serializtion(documentation).
All your classes will be desirialzied according to default rules.
Also i am changed your example a little bit to get it work(i've replaces myValueMap class with Dictionary):
public class myProdData
{
public Dictionary<string, myValue> Mapping { get; set; }
}
static void Main(string[] args)
{
var o = new mySystemPosition()
{
ProdData = new myProdData()
{
Mapping = new Dictionary<string, myValue>()
{
{"123", new myValue() {Id = 1, Label = "Item1"}},
{"345", new myValue() {Id = 2, Label = "Item2"}},
}
}
};
var json = o.ToJson();
Console.WriteLine(json);
Console.ReadKey();
}
Here is console output(just well formatted):
{
"Text":null,
"ProdData":{
"Mapping":{
"123":{
"_id":1,
"Label":"Item1"
},
"345":{
"_id":2,
"Label":"Item2"
}
}
}
}
You can test your serializtion using ToJson() extention method, in order to view that all correct and after that use ToBson() if need.
The problem is that myValueMap derives from Dictionary. That results in a class that the AutoMap method can't handle.
I recommend you just use the Dictionary directly, as Andrew did in his reply.
Ufortunately the myValueMap is an object that I can't easily change, however it turns out, that's pretty easy to create your own (de)serializer....
public class myValueMapSerializer : IBsonSerializer
{
public object Deserialize(Bson.IO.BsonReader bsonReader, System.Type nominalType, System.Type actualType, IBsonSerializationOptions options)
{
if (nominalType != typeof(myValueMap)) throw new ArgumentException("Cannot serialize anything but myValueMap");
var res = new myValueMap();
var ser = new DictionarySerializer<string, myValue>();
var dic = (Dictionary<string, myValue>)ser.Deserialize(bsonReader, typeof(Dictionary<string, myValue>), options);
foreach (var item in dic)
{
res.Add(item.Key, item.Value);
}
return res;
}
public object Deserialize(Bson.IO.BsonReader bsonReader, System.Type nominalType, IBsonSerializationOptions options)
{
throw new Exception("Not implemented");
}
public bool GetDocumentId(object document, out object id, out IIdGenerator idGenerator)
{
id = null;
idGenerator = null;
return false;
}
public void Serialize(Bson.IO.BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
if (nominalType != typeof(myValueMap)) throw new ArgumentException("Cannot serialize anything but myValueMap");
var ser = new DictionarySerializer<string, myValue>();
ser.Serialize(bsonWriter, typeof(DictionarySerializer<string, myValue>), value, options);
}
public void SetDocumentId(object document, object id)
{
return;
}
}