Unity Json.net bson Self referencing loop - unity3d

I am trying to save content in my game with Json.net. with this resource I got my game saving to JSON but now I want to save it in the Bson format as I don't want my players to be able to easily edit the save files.
Here is the code works and is saving my game data to json.
File.WriteAllText(path, JsonConvert.SerializeObject(objectToSave, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}));
Here I am trying to save my game data in the bson format but I don't quite know how to turn off the ReferenceLoopHandling in the bson format.
using (var stream = new MemoryStream())
{
var serializer = new JsonSerializer();
var writer = new BsonWriter(stream);
serializer.ReferenceLoopHandling.Equals(false);
serializer.Serialize(writer, objectToSave);
File.WriteAllText(path, serializer.ToString());
}
When I run this code I get the following error.
JsonSerializationException: Self referencing loop detected for property 'graph' with type 'StoryGraph'. Path 'nodes[0]'.

You can use the factory methods JsonSerializer.CreateDefault(JsonSerializerSettings) or JsonSerializer.Create(JsonSerializerSettings) to manufacture a serializer with your required settings, then serialize directly to a file using the following extension methods:
public static partial class BsonExtensions
{
// In Json.NET 10.0.1 and later use https://www.nuget.org/packages/Newtonsoft.Json.Bson
public static void SerializeToFile<T>(T obj, string path, JsonSerializerSettings settings = null)
{
using (var stream = new FileStream(path, FileMode.Create))
using (var writer = new BsonWriter(stream)) // BsonDataWriter in Json.NET 10.0.1 and later
{
JsonSerializer.CreateDefault(settings).Serialize(writer, obj);
}
}
public static T DeserializeFromFile<T>(string path, JsonSerializerSettings settings = null)
{
using (var stream = new FileStream(path, FileMode.Open))
using (var reader = new BsonReader(stream)) // BsonDataReader in Json.NET 10.0.1 and later
{
var serializer = JsonSerializer.CreateDefault(settings);
//https://www.newtonsoft.com/json/help/html/DeserializeFromBsonCollection.htm
if (serializer.ContractResolver.ResolveContract(typeof(T)) is JsonArrayContract)
reader.ReadRootValueAsArray = true;
return serializer.Deserialize<T>(reader);
}
}
}
And then serialize as follows:
BsonExtensions.SerializeToFile(objectToSave, path,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Notes:
Be sure to use the same settings when deserializing.
BSON support was moved into its own package Newtonsoft.Json.Bson in Json.NET 10.0.1. In this version or later you should use BsonDataWriter (and BsonDataReader) as BsonWriter has been made obsolete, and will eventually be removed.
serializer.ToString() is not going to return the serialized BSON; instead use MemoryStream.ToArray(), i.e.
File.WriteAllBytes(path, stream.ToArray());
However it's more efficient to stream directly to the file as shown in the extension methods above.
serializer.ReferenceLoopHandling.Equals(false); is not the correct way to set the ReferenceLoopHandling property in c#. Instead set it as if it were a field:
serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
See: Using Properties (C# Programming Guide).
Demo fiddle here.

You can also directly set the serializer:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Fix: Ignore loops
serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
...
This solved the issue for me in the Unity C# context.

Related

GenerateAppearances = true; System.NullReferenceException

Got some legacy code using iTextSharp.
All documents have GenerateAppearances set to true regardless. and now its triggering an exception.
Took the basic code out and placed it into a Console app, same thing, Used a generic PDF (http://www.pdf995.com/samples/pdf.pdf) of the net same thing.
This is using version 5.5.12
class Program
{
static void Main(string[] args)
{
var reader = new PdfReader(#"C:\Users\me\Desktop\pdf.pdf");
var outStream = new MemoryStream();
var stamper = new PdfStamper(reader, outStream);
stamper.AcroFields.GenerateAppearances = true; <--- usually true before setting
stamper.FormFlattening = true;
}
}
An unhandled exception of type 'System.NullReferenceException' occurred in itextsharp.dll
Additional information: Object reference not set to an instance of an object.
Thanks
After years of production, I just changed it from:
if (stamper.AcroFields != null)
{
f.GenerateAppearances = true;
foreach(var field in f.Fields)
{
f.SetField(field.Key, f.GetField(field.Key));
}
stamper.FormFlattening = true;
}
to
if (stamper.AcroFields != null && stamper.AcroFields.GenerateAppearances == true)
I encounter the same issues as below:
When I set AcroFields.GenerateAppearances = true
an unhandled exception of type 'System.NullReferenceException' occurred in itextsharp.dll
Additional information: Object reference not set to an instance of an object.
I debugged this code and found that AcroFields is not null. but NullReferenceException still occourred.
After inverstigating, I found that the format of PDF file is aspose xfa rather than acroforms. So I solved this issue by converting the format of PDF from aspose xfa to acroforms.

Plug-In that Converted Note entity pre-existing attachment XML file into new .MMP file

strong text [Plugin error at Note entity][1]
[1]: http://i.stack.imgur.com/hRIi9.png
Hi,Anyone resolved my issue i got a Plug-in error which i worked at Update of "Note" entity.Basically i want a Plugin which converted pre-exiting Note attachment XML file into new .MMP extension file with the same name.
I have done following procedure firstly i created a "Converter_Code.cs" dll which contains Convert() method that converted XML file to .MMP file here is the constructor of the class.
public Converter(string xml, string defaultMapFileName, bool isForWeb)
{
Xml = xml;
DefaultMapFileName = defaultMapFileName;
Result = Environment.NewLine;
IsForWeb = isForWeb;
IsMapConverted = false;
ResultFolder = CreateResultFolder(MmcGlobal.ResultFolder);
}
In ConvertPlugin.cs Plug-in class firstly i retrieved Note entity attachment XML file in a string using following method in
IPluginExecutionContext context =(IPluginExecutionContext)serviceProvider.
GetService (typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory= (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService
(context.UserId);
if (context.InputParameters.Contains("Target")
&& context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
var annotationid = entity.GetAttributeValue<Guid>("annotationid");
if (entity.LogicalName != "annotation")
{
return;
}
else
{
try
{
//retrieve annotation file
QueryExpression Notes = new QueryExpression { EntityName ="annotation"
,ColumnSet = new ColumnSet("filename", "subject", "annotationid",
"documentbody") };
Notes.Criteria.AddCondition("annotationid", ConditionOperator.Equal,
annotationid);
EntityCollection NotesRetrieve = service.RetrieveMultiple(Notes);
if (NotesRetrieve != null && NotesRetrieve.Entities.Count > 0)
{
{
//converting document body content to bytes
byte[] fill = Convert.FromBase64String(NotesRetrieve.Entities[0]
.Attributes["documentbody"].ToString());
//Converting to String
string content = System.Text.Encoding.UTF8.GetString(fill);
Converter objConverter = new Converter(content, "TestMap", true);
objConverter.Convert();
}
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("something is going wrong");
}
}
}
}
and than A string is passed to "Converter" constructor as a parameter.
finally i merged all dll using ILMerge following method:
ilmerge /out:NewConvertPlugin.dll ConvertPlugin.dll Converter_Code.dll
and NewConvertPlugin is registered successfully but while its working its generate following error:
Unexpected exception from plug-in (Execute): ConverterPlugin.Class1: System.Security.SecurityException: That assembly does not allow partially trusted callers.
i also debug the plugin using Error-log but its not worked so i could not get a reason whats going wrong.
The error is caused by the library you merged inside your plugin.
First of all you are using CRM Online (from your screenshot) and with CRM Online you can use only register plugins inside sandbox.
Sandbox means that your plugins are limited and they run in a partial-trust environment.
You merged an external library that requires full-trust permissions, so your plugin can't work and this is the reason of your error.
Because you are in CRM Online, or you find another library (the Converter) that requires only partial-trust, hoping that the merge process will work, or you include (if you have it) the source code of the converter library directly inside your plugin.

CRM 2013: Deserializing within a sandboxed plugin

I have an XML string that I would like to deserialize into a strongly typed class. The below code works great until I put it into a sandboxed plugin, at which point I get a FileIOPermissions error because I am using the StringReader class. I am having issues trying to deserialize without using StringReader. Does anyone have a good alternative?
byte[] binary = Convert.FromBase64String(configurationWebResource.Attributes["content"].ToString());
resourceContent = UnicodeEncoding.UTF8.GetString(binary);
DataContractSerializer serializer = new DataContractSerializer(typeof(ViewSecurityConfiguration));
using (StringReader reader = new StringReader(resourceContent))
{
using (XmlTextReader xmlReader = new XmlTextReader(reader))
{
if (serializer.IsStartObject(xmlReader)) //Throws FileIOPermissions error
{
currentViewSecurityConfiguration = (ViewSecurityConfiguration)(serializer.ReadObject(xmlReader));
}
}
}
Try the following which I've run successfully in a sandbox plugin:
byte[] binary = Convert.FromBase64String(configurationWebResource.Attributes["content"].ToString());
resourceContent = UnicodeEncoding.UTF8.GetString(binary);
XmlSerializer serializer = new XmlSerializer(typeof(ViewSecurityConfiguration));
using (StringReader reader = new StringReader(resourceContent))
{
currentViewSecurityConfiguration = (ViewSecurityConfiguration)serializer.Deserialize(reader);
}

Reading from an assembly with embedded resources

I built an assembly containing one js file.
I marked the file as Embedded Resource and added it into AssemblyInfo file.
I can't refernce the Assembly from a web site. It is in the bin folder but I don't see the reference to it.
It seems like not having at least a class inside the assembly I can't reference it.
I would include the js file into my pages from the assembly.
How should I do this?
Thanks
I do exactly the same thing in one of my projects. I have a central ScriptManager class that actually caches the scripts as it pulls them, but the core of extracting the script file from the embedded resource looks like this:
internal static class ScriptManager
{
private static Dictionary<string, string> m_scriptCache =
new Dictionary<string, string>();
public static string GetScript(string scriptName)
{
return GetScript(scriptName, true);
}
public static string GetScript(string scriptName, bool encloseInCdata)
{
StringBuilder script = new StringBuilder("\r\n");
if (encloseInCdata)
{
script.Append("//<![CDATA[\r\n");
}
if (!m_scriptCache.ContainsKey(scriptName))
{
var asm = Assembly.GetExecutingAssembly();
var stream = asm.GetManifestResourceStream(scriptName);
if (stream == null)
{
var names = asm.GetManifestResourceNames();
// NOTE: you passed in an invalid name.
// Use the above line to determine what tyhe name should be
// most common is not setting the script file to be an embedded resource
if (Debugger.IsAttached) Debugger.Break();
return string.Empty;
}
using (var reader = new StreamReader(stream))
{
var text = reader.ReadToEnd();
m_scriptCache.Add(scriptName, text);
}
}
script.Append(m_scriptCache[scriptName]);
if (encloseInCdata)
{
script.Append("//]]>\r\n");
}
return script.ToString();
}
}
EDIT
To provide more clarity, I've posted my ScriptManager class. To extract a script file, I simply call it like this:
var script = ScriptManager.GetScript("Fully.Qualified.Script.js");
The name you pass in it the full, case-sensitive resource name (the exception handler gets a list of them by calling GetManifestResourceNames()).
This gives you the script as a string - you can then put it out into a file, inject it into the page (which is what I'm doing) or whatever you like.
Assembly myAssembly = // Get your assembly somehow (see below)...
IList<string> resourceNames = myAssembly.GetManifestResourceNames();
This will return a list of all resource names that have been set as 'Embedded Resource'. The name is usually the fully qualified namespace of wherever you put that JS file. So if your project is called My.Project and you store your MyScript.js file inside a folder in your project called Resources, the full name would be My.Project.Resources.MyScript.js
If you then want to use that JS file:
Stream stream = myAssembly.GetManifestResourceStream(myResourceName);
Where myResourceName argument might be "My.Project.Resources.MyScript.js". To get that JS file in that Stream object, you'll need to write it as a file to the hard drive, and serve it from your website as a static file, something like this:
Stream stream = executingAssembly.GetManifestResourceStream(imageResourcePath);
if (stream != null)
{
string directory = Path.GetDirectoryName("C:/WebApps/MyApp/Scripts/");
using (Stream file = File.OpenWrite(directory + "MyScript.js"))
{
CopyStream(stream, file);
}
stream.Dispose();
}
And the code for CopyStream method:
private static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
You might want to stick all this code in an Application_Start event in your Global.asax. You don't want it to run for each request
Now getting a reference to your Assembly is a different matter, there are many ways. One way is to include all the above code in your Assembly in question, then make sure you reference that Assembly from your main WebApp project in Visual Studio, then get a reference to the currently executing Assembly like so.
namespace My.Project
{
public class ResourceLoader
{
public static void LoadResources()
{
Assembly myAssembly = Assembly.GetExecutingAssembly();
// rest of code from above (snip)
}
}
}
Then call ResourceLoader.LoadResources() from your Application_Start event in your Global.asax.
Hope this helps
Fully working example (I hope):
namespace TestResources.Assembly
{
public class ResourceLoader
{
public static void LoadResources()
{
Assembly myAssembly = Assembly.GetExecutingAssembly();
Stream stream = myAssembly
.GetManifestResourceStream("TestResources.Assembly.CheckAnswer.js");
if (stream != null)
{
string directory = Path.GetDirectoryName("C:/WebApps/MyApp/Scripts/");
using (Stream file = File.OpenWrite(directory + "MyScript.js"))
{
CopyStream(stream, file);
}
stream.Dispose();
}
}
private static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
}
}
Some caveats:
Change "C:/WebApps/MyApp/" to wherever your web app is located, maybe write some code to work this out dynamically
Make sure the /Scripts folder exists in your webapp root
I think it will overwrite the 'MyScript.js' file if it already exists, but just in case you might want to add some code to check for that file and delete it
Then stick a call to this code in your Global.asax file:
protected void Application_Start()
{
ResourceLoader.LoadResources();
}
Then the path for your web site will be /Scripts/MyScript.js eg:
<head>
<!-- Rest of head (snip) -->
<script type="text/javascript" src="/Scripts/MyScript.js"></script>
</head>

Debugging JSON in GWT (Cross Server)

How do I pass the jsonObj from the javascript code in getJson to the java code handleJsonResponse. If my syntax is correct, what do I do with a JavaScriptObject?
I know that the jsonObj contains valid data because alert(jsonObj.ResultSet.totalResultsAvailable) returns a large number :) --- but some how it's not getting passed correctly back into Java.
EDIT: I solved it... by passing in jsonObj.ResultSet.Result to the java function and working on it using a JSONArray.
Thanks.
public native static void getJson(int requestId, String url, MyClass handler) /*-{
alert(url);
var callback = "callback" + requestId;
var script = document.createElement("script");
script.setAttribute("src", url+callback);
script.setAttribute("type", "text/javascript");
window[callback] = function(jsonObj) { // jsonObj DOES contain data
handler.#com.michael.random.client.MyClass::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
window[callback + "done"] = true;
}
document.body.appendChild(script);
}-*/;
public void handleJsonResponse(JavaScriptObject jso) { // How to utilize JSO
if (jso == null) { // Now the code gets past here
Window.alert("Couldn't retrieve JSON");
return;
}
Window.alert(jso.toSource()); // Alerts 'null'
JSONArray array = new JSONArray(jso);
//Window.alert(""+array.size());
}
}
Not exactly sure how to fix this problem that I had, but I found a workaround. The javascript jsonObj is is multidimensional, and I didn't know how to manipulate the types in the java function. So instead, I passed jsonObj.ResultSet.Result to my function handler, and from there I was able to use get("string") on the JSONArray.
What is toSource() supposed to do? (The documentation I see for it just says "calls toSource".) What about toString()?
If your call to alert(jsonObj.ResultSet.totalResultsAvailable) yields a valid result, that tells me jsonObj is a JavaScript Object, not an Array. Looks to me like the constructor for JSONArray only takes a JS Array (e.g., ["item1", "item2", {"key":"value"}, ...] )