IXmlSerializable and Root Element Prefix - xmlserializer

I have looked everywhere and not a single response is valid or the question is just slightly off to the point of not getting me the answer I need. Given all the searches I have looked for there seems to be one MASSIVE flaw in .Net's implementation of xml serialization.
Default:
[XmlRoot("root", Namespace="http://myclass.org/")]
public class MyClass
{
}
void Main() {
XmlSerializer ser = new XmlSerializer(typeof(MyClass));
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
xsn.Add("mc", "http://myclass.org/");
ser.Serialize(new StreamWriter(Console.Out), new MyClass(), xsn);
}
OUTPUT:
<?xml version="1.0"?>
<mc:root xmlns:mc="http://myclass.org/">
</mc:root>
IXmlSerializable:
[XmlRoot("root", Namespace="http://myclass.org/")]
public class MyClass : IXmlSerializable
{
public XmlSchema GetSchema() {return null;}
public void ReadXml(XmlReader reader) {}
public void WriteXml(XmlWriter writer) {}
}
void Main() {
XmlSerializer ser = new XmlSerializer(typeof(MyClass));
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
xsn.Add("mc", "http://myclass.org/");
ser.Serialize(new StreamWriter(Console.Out), new MyClass(), xsn);
}
OUTPUT:
<?xml version="1.0"?>
<root xmlns="http://myclass.org/">
</root>
WHY!
How do we fix this?
This is essential to figure out because without custom processing I will be forced to double step it, and process the serialized xml into an XmlDocument to fix this glitch. and yes, this HAS to be a glitch. I can work around everything else EXCEPT the root element.
I am not the only one who needs to know how to do this.
Thanks
Jaeden "Sifo Dyas" al'Raec Ruiner

Related

Why is .NET MAUI Dependency Injection crashing my app?

I'm trying to follow this article on .NET MAUI dependency injection.
My MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddSingleton<IDataService, DataService>();
builder.Services.AddTransient<NavigationService>();
builder.Services.AddTransient<ValidationService>();
builder.Services.AddSingleton<BudgetViewPage>();
builder.Services.AddSingleton<BudgetViewModel>();
builder.Services.AddSingleton<AccountsViewModel>();
builder.Services.AddSingleton<FlyoutMenuRoot>();
return builder.Build();
}
}
My App.xaml.cs
public partial class App : Application
{
public App(FlyoutMenuRoot flyoutMenuRoot)
{
InitializeComponent();
MainPage = flyoutMenuRoot;
}
}
My FlyoutMenuRoot.xaml.cs
public partial class FlyoutMenuRoot : FlyoutPage
{
IDataService dataService;
BudgetViewModel budgetViewModel;
private NavigationService NavigationService = new();
public FlyoutMenuRoot(IDataService dataService, BudgetViewModel budgetViewModel)
{
InitializeComponent();
this.dataService = dataService;
this.budgetViewModel = budgetViewModel;
Detail = new NavigationPage(new BudgetViewPage(budgetViewModel));
flyoutMenuRoot.flyoutCollectionView.SelectionChanged += OnSelectionChanged;
}
void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var item = e.CurrentSelection.FirstOrDefault() as FlyoutPageItem;
if(item != null)
{
if(item.TargetType == typeof(SelectAccountPage))
{
NavigationService.PushToStack((Page)Activator.CreateInstance(item.TargetType, new AccountsViewModel(dataService, budgetViewModel)));
}
else
{
NavigationService.PushToStack((Page)Activator.CreateInstance(item.TargetType));
}
this.IsPresented = false;
flyoutMenuRoot.flyoutCollectionView.SelectedItem = null;
}
}
}
Based on the linked article, this should work, but my app crashes on the splash screen.
If my App.xaml.cs is this:
public partial class App : Application
{
public App()
{
InitializeComponent();
DataService dataService = new();
BudgetViewModel budgetViewModel = new(dataService);
MainPage = new FlyoutMenuRoot(dataService, budgetViewModel);
}
}
Then it works with no problem.
My understanding is that you shouldn't have to new() up an instance of your classes with Dependency Injection, that the container will do it automatically for you based on what's listed in the constructor. I'm following the article, so why is it crashing?
Edit:
I stepped through the code and narrowed the crash down to the InitializeComponent() call under FlyoutMenuPage()
public partial class FlyoutMenuPage : ContentPage
{
public FlyoutMenuPage()
{
try
{
InitializeComponent();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
The message written to the output window is:
[DOTNET] Position 11:5. StaticResource not found for key Primary
That's referencing this line in the FlyoutMenuPage.xaml
BackgroundColor="{StaticResource Primary}"
This is confounding because that line never threw an exception until I tried following the method for DI from the article. If I go back to constructor injection, it doesn't crash.
First.
DI is there for you, so you don't have to construct your classes manually. (As I told you yesterday) You add classes as Singletons, and at the same time, you are manually constructing them. DI will call those constructions when needed. Your idea that they are "never initialized" and you have to do it at least once is wrong.
Second.
DI is not "crashing" your app. If anything, not injecting services and/or ViewModels cause runtime exceptions. (When you try to navigate to something, that uses such Type in its constructor for example.) Not the oposite.
Third.
DI has very little to do with XAML/Resources problems. Especially with your BackgroundColor problem.
So I'm experiencing a logical issue. Take this App for example:
public partial class App : Application
{
public App(MainPage mp, MainPageViewModel vm)
{
InitializeComponent();
MainPage = mp; // new MainPage(); //AppShell();
MainPage.BindingContext= vm;
}
}
And in the App.xaml, we have this:
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MVVMDI"
x:Class="MVVMDI.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Because I have MainPage in my constructor, ready for dependency injection, the xaml in MainPage gets loaded up before the xaml in App. So, with that in mind, App.xaml hasn't yet registered the Colors.xaml because InitializeComponent hasn't been called yet. So because MainPage is trying to reference StaticResource Primary, which is in Colors.xaml, which hasn't been registered yet, I get my exception.
This answers my question as to why DI is making my app crash.
(So to solve this, I just need to find a way to register Colors.xaml application-wide like App does....)

Remove new lines from Message and Exception

I am using Serilog in an Asp dot net core application. I want my my log files to be human readable, but also be able to be parsed easily.
The problem that I have come across is that exceptions are logged with line breaks. Some Microsoft events have messages that include new lines.
I would like to be able to parse the log file with one event per line.
I could write my own implementation of ITextFormatter that replaces new lines with \r\n, but that we mean I would need to duplicate much of the logic in MessageTemplateTextFormatter and other classes.
After digging into this for a while, I was able to come up with an answer. Andy West's answer pointed me in the right direction.
There are two separate issues here: CRLFs in the message and CRLFs in the exception.
I was able to solve the message problem by changing "{Message}" to "{Message:j}" in the outputTemplate.
Changing the exception was a little trickier. I had to add an enricher:
class ExceptionEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.Exception == null)
return;
var logEventProperty = propertyFactory.CreateProperty("EscapedException", logEvent.Exception.ToString().Replace("\r\n", "\\r\\n"));
logEvent.AddPropertyIfAbsent(logEventProperty);
}
}
This adds and new property called EscapedException. This has to be added to the configuration with .Enrich.With().
Then I replaced "{Exception}" with "{EscapedException}" in the outputTemplate.
This technique will remove all CRLF. First a new ITextFormatter.
public class RemoveCrLf : ITextFormatter
{
private const int DefaultWriteBuffer = 256;
private readonly ITextFormatter _textFormatter;
/// <summary>
///
/// </summary>
/// <param name="textFormatter"></param>
public RemoveCrLf(ITextFormatter textFormatter)
{
_textFormatter = textFormatter;
}
/// <summary>
///
/// </summary>
/// <param name="logEvent"></param>
/// <param name="output"></param>
public void Format(LogEvent logEvent, TextWriter output)
{
var buffer = new StringWriter(new StringBuilder(DefaultWriteBuffer));
_textFormatter.Format(logEvent, buffer);
var logText = buffer.ToString();
output.WriteLine(logText.Trim().Replace("\n","\\n").Replace("\r","\\r"));
output.Flush();
}
}
Use it like this
configuration.WriteTo.Console(new RemoveCrLf(new MessageTemplateTextFormatter("[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Exception}")));
Of course, customise the output template as needed.
In reference to the solution from #Andrew Radford, that solution was working for me only on windows, but not on linux docker environment as expected. Therefore
I have enhanced the solution for messages as well as exceptions.
I have used Environment.NewLine for case matching in Regex which will pick the case based on the hosted environment.
Add following class for Messages.
public class MessageEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.MessageTemplate == null)
return;
var logEventProperty = propertyFactory.CreateProperty("EscapedMessage", Regex.Replace(logEvent.MessageTemplate.ToString(), Environment.NewLine, "[Newline]"));
logEvent.AddPropertyIfAbsent(logEventProperty);
}
}
Use following class for Exceptions.
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.Exception == null)
return;
var logEventProperty = propertyFactory.CreateProperty("EscapedException", Regex.Replace(logEvent.Exception.ToString(), Environment.NewLine, "[Newline]"));
logEvent.AddPropertyIfAbsent(logEventProperty);
}
Then add these both helper classes into program.cs
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.With(new ExceptionEnricher())
.Enrich.With(new MessageEnricher())
.Enrich.FromLogContext()
.CreateLogger();
Update the appsettings output template
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u4} {SourceContext:l} : {EscapedMessage}{NewLine}{EscapedException}{NewLine}"
You didn't specify how you are logging exceptions, but assuming you are using something like:
Log.Error("An error occurred: {Exception}", exception);
then the exception will be rendered using ToString(). In that case, couldn't you simply do this instead:
Log.Error("An error occurred: {Exception}", exception.ToString().Replace(System.Environment.NewLine, "\\r\\n"));
Of course, you could refactor this into a method (maybe an extension method) if you need to do it more than one place.

How can I dynamically alter the files in the /Content directory in ASP.NET MVC?

When I serve a javascript file to a user from the /Content Directory I want to replace a string token in that file with a value, so that when the user requests a given file, it has all the customizations they expect.
I think that means I need to somehow proxy requests to the /Content directory, perform the dynamic insertion, and give the file to the user.
I'm interested in performing this insertion as a stream or as a in -memory file. I'd prefer to use a stream just because it's probably more efficient memory wise.
How do I get ASP.NET to proxy this directory?
I've attempted
Using routes to point to a controller
WCF to proxy a URL
But they all seem "ugly" to me and I'd like to make this insertion/replacement as transparent as possible int he project.
Is there a cleaner way?
The easiest way is to create an action on a controller.
public class JavascriptController : Controller
{
public ActionResult Load(string file)
{
var content = System.IO.File.ReadAllText(Server.MapPath(string.Format("~/Content/{0}", file)));
//make replacements io content here
return this.Content(content, "application/javascript");
}
}
You can then access the javascript like this (assuming you have the default routing):
http://localhost:53287/Javascript/Load?file=file.js
where file.js is the name of the file you are requesting.
Don't worry about the url, you can customise this by creating another route if necessary
Here is alternative answer to the answer I posted above, taking into account your comment regarding dynamic javascript.
Firstly, I don't know of a way to do this specifically using either mvc or wcf.. the only way I know how to do this is with a lower-level HttpModule
Take a look at the following code:
public class JavascriptReplacementModule : IHttpModule
{
public class ResponseFilter : MemoryStream
{
private Stream outputStream = null;
public ResponseFilter(Stream output)
{
outputStream = output;
}
public override void Flush()
{
base.Flush();
this.Seek(0, SeekOrigin.Begin);
var sr = new StreamReader(this);
string contentInBuffer = sr.ReadToEnd();
//Do replacements here
outputStream.Write(UTF8Encoding.UTF8.GetBytes(contentInBuffer), 0, UTF8Encoding.UTF8.GetByteCount(contentInBuffer));
outputStream.Flush();
}
protected override void Dispose(bool disposing)
{
outputStream.Dispose();
base.Dispose(disposing);
}
}
public void Dispose() { }
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
}
void context_PostRequestHandlerExecute(object sender, EventArgs e)
{
var context = (HttpApplication)sender;
if (context.Request.Url.AbsolutePath.StartsWith("/Content") && context.Request.Url.AbsolutePath.EndsWith(".js"))
{
HttpContext.Current.Response.Filter = new ResponseFilter(HttpContext.Current.Response.Filter);
}
}
}
And register the module like this (make sure you put the full type in the type attribute):
<system.webServer>
<modules>
<add name="JavascriptReplacementModule" type="JavascriptReplacementModule"/>
</modules>
</system.webServer>
This allows you to modify the output stream before it gets to the client

Putting parameters in velocity context in Jira 4.4

I'm developing a plugin to display additional information related to a project.
So I'm developing a Project Tab Panel module but my page does not display the paramenters I put in the velocity context.
Here is the a part of plugin xml:
<project-tabpanel key="stats-tab-panel" name="Stats Tab Panel" i18n-name-key="stats-tab-panel.name" class="it.pride.jira.plugins.StatsTabPanel">
<description key="stats-tab-panel.description">The Stats Tab Panel Plugin</description>
<label key="stats-tab-panel.label"></label>
<order>10</order>
<resource type="velocity" name="view" location="templates/tabpanels/stats-tab-panel.vm"/>
Here instead the useful part of my class:
public class StatsTabPanel extends GenericProjectTabPanel {
public StatsTabPanel(JiraAuthenticationContext jiraAuthenticationContext,
FieldVisibilityManager fieldVisibilityManager) {
super(jiraAuthenticationContext, fieldVisibilityManager);
// TODO Auto-generated constructor stub
}
public String testvalue="112002";
#Override
public boolean showPanel(BrowseContext context){
return true;
}
#Override
public Map<String, Object> createVelocityParams (BrowseContext context) {
Map<String, Object> contextMap = createVelocityParams(context);
contextMap.put("testvalue", testvalue);
return contextMap;
}
}
So, as in this case, when i write `$testvalue in my template the number doesn't show up.
What am I doing wrong?
The problem is that the getHtml method that is used to return the HTML for the tab has a Map that is the Velocity context but only contains the params in the BrowseContext object. The way to fix this is to override createVelocityParams in your class, e.g.
protected Map createVelocityParams(final BrowseContext ctx) {
Map params = super.createVelocityParams(ctx);
params.put("myparam", "some value for it");
return params;
}
The problem is that method is protected so I ended up also overriding getHtml which calls createVelocityParams in a parent class.
Some Project tabs extend GenericProjectTabPanel but some such as SummaryProjectTabPanel.java seem to use UI Fragments. I suspect that Fragments is what things are moving towards.

Single action class for multiple dynamic URIs throws exception when concurrent requesting

I've developed a web site using Struts2 as a controller and integrated it with Spring and Hibernate to do the business logic and DB stuff. The website's URIs are http://my.domian.com/URI; which {URI} is dynamically generated thorough the admin cms. The mapping of each uri to the servlet are done with help of Apache mod_rewrite, as follow:
RewriteCond %{HTTP_HOST} ^www\.domain\.com
RewriteRule ^([a-zA-Z0-9_-]+)$ /dynamic\.action?f=$1 [QSA,L]
(Before any further information, is this a good and suitable approach?)
The struts configuration is just a typically-academic one as:
<package name="Default" extends="struts-default" namespace="/">
...
<action name="dynamic" class="DynamicContentAction">
<result name="index">/content/web/dynamic/index.jsp</result>
</action>
</package>
DynamicContentAction is extending ActionSupport and implementing ServletRequestAware, ServletContextAware. I'm checking a couple of things (such as a current visiting language which is identified as a subdomain), looking up in the database that the requested uri is valid or not, generating that uri's content and setting a couple of runtime global variables (such as current visiting page id, layout config due to current visiting language ...) and put it on a Request object in this servlet.
Everything looks good and even works perfectly ok, unless too many dynamic pages being requested by a single user concurrently. "Too Many" in my case is at least 9-10 pages. In this case it throws exceptions, different ones! Sometimes the HttpServletRequest request is null, sometimes ServletContext servletContext is null, some other times these are ok, but the runtime variables are null which is used in business logic or db querying.
I've googled about it and found out that this action is being instantiated "Per Request". Isn't this so? If there is an action per request, what's wrong with this conflict or "nullability thing". Should I do some thread-like thing in that action, beyond the threading of struts?
I'd be so appreciated if you could help me out or point me a direction.
Here is simplified version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport implements ServletRequestAware, ServletContextAware {
private HttpServletRequest request;
private ServletContext servletContext;
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
}
else {
//Generating nice 404 error page content
}
request = null;
servletContext = null;
f = null;
return "index";
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
as I'm writing this post, I have came to the knowledge that this class is NOT thread-safe. Is it? So I've changed it a little bit as follow:
Here is newer version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport {
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
final HttpServletRequest request = ServletActionContext.getRequest();
final ServletContext servletContext = ServletActionContext.getServletContext();
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
f = null;
}
else {
//Generating nice 404 error page content
}
return "index";
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
and the Null thing problem is almost gone, but there is still conflict with the generated content. For example if user try to open:
http:// www.domain.com/A
http:// www.domain.com/B
http:// www.domain.com/C
http:// www.domain.com/D
http:// www.domain.com/E
simultaneously, all of the pages will be rendered in browser, but the content of A is shown in A and B, C is correct, and there is a very good chance that the content of D and E are incorrect too.

Resources