How to use a relative path in XDocument.Load with Xamarin? - xamarin

I always get the system not found exception. How can I find a xml file in a bundle of my xamarin.android application?
public Dictionary<string, string> TilePropertiesForTileID(short tileGid)
{
Dictionary<string, string> propertiesDict = null;
try
{
// Loading from a file, you can also load from a stream
var xml = XDocument.Load(Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Assets/Content/tileMap/wood_tileset.tsx"));
// Query the data and write out a subset of contacts
var propertiesQuery = from c in xml.Root.Descendants("tile")
where (int)c.Attribute("id") == tileGid
select c.Element("property").Value;
foreach (string property in propertiesQuery)
{
Console.WriteLine("Property: {0}", property);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw e;
}
return propertiesDict;
}
This line:
var xml = XDocument.Load(Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Assets/Content/tileMap/wood_tileset.tsx"));
always throws the exception. I don't know How to get access to this file.
updated: Thanks to apineda I am now able to find the file : here is the xml file
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.1" name="wood_tileset"
tilewidth="32" tileheight="32" tilecount="256" columns="16">
<image source="wood_tileset.png" width="512" height="512"/>
<tile id="68">
<properties>
<property name="IsTreasure" value="true"/>
</properties>
</tile>
</tileset>

For this you don't necessarily need the Full Path of the File. Since you placed the file in the Assets directory and I hope also marked it as AndroidAsset in the Build Action all you need is to make use of the AssetManager.
If you are within an Activity you can open a file in the Assets like this:
Assets.Open("fileName.ext")
If you added subdirectories in the Assets directory you will need to include the full path (without including the word "Assets").
Using your code as sample, it should look like this:
using (var sReader = new StreamReader(Assets.Open("Content/titleMap/wood_tileset.tsx")))
{
var xmlDoc = XDocument.Load(sReader);
// Query the data and write out a subset of contacts
var propertiesQuery = xmlDoc.Root.Descendants("tile")
.Where(item => (int)item.Attribute("id") == tileGid)
.SelectMany(a => a.Descendants("property"))
.Select(property => new
{
Name = property.Attribute("name").Value,
Value = property.Attribute("value").Value
})
.ToList();
foreach (var property in propertiesQuery)
{
Console.WriteLine($"Property Name: {property.Name}, Value: {property.Value}");
}
}
As you can see the return of the Assets.Open method is passed into a StreamReader. The using is not necessary but this will prevent leaving resources opened.
The StreamReader is then passed in to the Load() method of the XDocument and the rest is exactly as you had it.
Note: This assume you have a file structure like the following:
Assets
Content
titleMap
wood_titleset.tsx
Hope this helps.-

Related

ASP.NET Core 6 MVC : read xml read outerxml remove tags

I am writing an ASP.NET Core 6 MVC app, and I have to read an XML with this format:
<?xml version="1.0" encoding="utf-8" ?>
<taglines common-copyright="©2007 ">
<tagline brandID="1" brandName="BCBS" >
<registered registered="®Registered">
</registered>
<copyright copyright="©2007">
</copyright>
<tagline2 tagline2="Data.">
</tagline2>
</tagline>
</taglines>
I need to read the content in tag tagline2
The way I am doing it is
private string ReadXMLAnthemFile()
{
string footer = "";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(String.Concat(this.Environment.WebRootPath,"/img/TagLines.xml"));
foreach (XmlNode node in xmlDocument.SelectNodes("taglines/tagline/tagline2"))
{
footer = node.OuterXml;
}
return footer;
}
But node.OuterXml returns the entire tag
<tagline2 tagline2="Data."></tagline2>
How can I get only the data inside the tag?
Thanks
You don't need to read the "outer" XML - you need to reach into the XML node you've fetched, and read it's tagline2 attribute - if present.
Try something like this:
foreach (XmlNode node in xmlDocument.SelectNodes("taglines/tagline/tagline2"))
{
if (node.Attributes["#tagline2"] != null)
{
footer = node.Attributes["#tagline2"].Value;
}
}
That should get you the .Value of the tagline2 attribute of the XML node that your XPath expression matches - the value would be Data. in your case here.

Uploading images with redactor to MVC

This might be a bit too specific for here and I may need to contact redactor support but i've seen other questions about redactor here so i figured i'd give it a shot ...
Ok ...
So i'm trying to get get image uploading to work following the example here ...
http://imperavi.com/redactor/docs/images/
My client side code ...
$("textarea").redactor({
focus: true,
imageUpload: '/MyController/UploadImage'
});
My MVC controller action looks like this ...
public JsonResult UploadImage(object image)
{
// Do something with whatever that was i got from redactor
var result = new { filelink = "" };
return Json(result);
}
The problem is ... what did redactor actually give me?
Was it the whole file? a chunk? i can't seem to tell because the object has no type information at all and the raw post information seems way too little to actually be a whole image file.
Has anyone had any experience with this / actually done it before?
I don't really want to setup php on my server for this 1 function.
EDIT:
Ok a bit more digging reveals that if i pull the underlying Request object it has a files property which apparently contains my posted image file.
I think i might be able to figure it out from here.
Where I get a code block in place i'll post it as an answer.
You are receiving a HttpPostedFileBase object. Here is my implementation:
jQuery:
$('#blog-post').redactor(
{
imageUpload: '/blog/images/',
imageGetJson: '/images/locations/blogs/'
});
Then in the controller:
public ActionResult Images(HttpPostedFileBase file)
{
// Verify that the user selected a file
if( file != null && file.ContentLength > 0 )
{
// extract only the fielname
var fileName = Path.GetFileName( file.FileName );
// store the file
var path = Path.Combine( ImageLocation.BlogPicturePath, fileName );
file.SaveAs( path );
}
return Json( new { filelink = ImageLocation.BlogPictureUrl + "/" + file.FileName } );
}
ok um ... i think im there ...
This needs a bit of cleaning up and I don't expect you guys to understand what goes on under the bonnet of my custom DMS code but just assume it takes the stream and returns a FileInfo object and in theory this should work for you too ...
public ActionResult Upload()
{
// this object is specific to my system but all it does is
// stream the file to a path on the server (code not needed for this Q)
var dmsService = _kernel.Get<IDMSFileSystemService>();
List<FileInfo> savedFiles = new List<FileInfo>();
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
using (file.InputStream)
{
savedFiles.Add(dmsService.AddFromStream(file.InputStream, file.FileName);
}
}
var result = savedFiles.Select(f => new { filelink = f.Path}).ToArray();
return Json(result);
}
Suprisingly simple right ... :)

unable to get multiple entries in SelectEntries of DotNetZip

I'm trying to use DotNetZipLib-DevKit-v1.9 in my MVC3 Project to extract the files to a specific folder.
What i want is -- How to add multiple entries in zip.SelectEntries method.
Here is my code in controller action:
public ActionResult ExtractZip(string fileName, HttpPostedFileBase fileData)
{
string zipToUnpack = #"C:\Users\Public\Pictures\Sample Pictures\images.zip";
string unpackDirectory = System.IO.Path.GetTempPath();
using (ZipFile zip1 = ZipFile.Read(zipToUnpack))
{
// here, we extract every entry, but we could extract conditionally
// based on entry name, size, date, checkbox status, etc.
var collections = zip1.SelectEntries("name=*.jpg;*.jpeg;*.png;*.gif;");//This shows `0` items in collections
foreach (var item in collections)
{
item.Extract(unpackDirectory, ExtractExistingFileAction.OverwriteSilently);
}
}
return Json(true);
}
In this line var collections = zip1.SelectEntries("name=*.jpg;*.jpeg;*.png;*.gif;"); if i specify only single extension ,it works fine
ex:
var collections = zip1.SelectEntries("name=*.gif"); this works good
I've also seen SelectEntries method here, but it doesn't help though
How to add multiple entries ?
Finally i could answer my own question.
Inorder to select multiple entries we need to use OR and to select multiple entries use the following code:
var collections = zip1.SelectEntries("(name=*.jpg) OR (name=*.jpeg) OR (name=*.png) OR (name=*.gif)");
foreach (var item in collections)
{
item.Extract(unpackDirectory, ExtractExistingFileAction.OverwriteSilently);
}

unable to parse the xml query using Linq

I am developing a sample Twitter app for Windows phone 7. In my code to display some details of user, used the following code.
void ShowProfile()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Profile_DownloadCompleted);
client.DownloadStringAsync(new Uri("http://api.twitter.com/1/users/show.xml?user_id=" + this.id));
}
void Profile_DownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{ return; }
if (e.Result == null) MessageBox.Show("NUlllllllllll");
XElement Profile = XElement.Parse(e.Result);
var ProfileDetails = (from profile in Profile.Descendants("user")
select new UserProfile
{
UserName = profile.Element("screen_name").Value,
ImageSource = profile.Element("profile_image_url").Value,
Location = profile.Element("location").Value,
TweetsCount = profile.Element("statuses_count").Value,
}).FirstOrDefault();
LayoutRoot.DataContext = ProfileDetails;
}
Here, LayoutRoot is the Grid name. But the data binding doesn't work.
Infact, when kept a Break point it seems there is no data in the ProfileDetails object. But I could observe that e.Result contains the required data in the XML format.
Can any body figureout where I am going wrong??
Thanks in advance.
You have used XElement.Parse so Profile represents the single root <user> that API request would have returned. You are then trying to look for user elements inside it which of course makes no sense.
Try XDocument.Parse instead. Also does it really make any sense assigning a IEnumerable<UserProfile> to the data context when that list can only ever contain 1 entry?

A better solution than element.Elements("Whatever").First()?

I have an XML file like this:
<SiteConfig>
<Sites>
<Site Identifier="a" />
<Site Identifier="b" />
<Site Identifier="c" />
</Sites>
</SiteConfig>
The file is user-editable, so I want to provide reasonable error message in case I can't properly parse it. I could probably write a .xsd for it, but that seems kind of overkill for a simple file.
So anyway, when querying for the list of <Site> nodes, there's a couple of ways I could do it:
var doc = XDocument.Load(...);
var siteNodes = from siteNode in
doc.Element("SiteConfig").Element("Sites").Elements("Site")
select siteNode;
But the problem with this is that if the user has not included the <SiteUrls> node (say) it'll just throw a NullReferenceException which doesn't really say much to the user about what actually went wrong.
Another possibility is just to use Elements() everywhere instead of Element(), but that doesn't always work out when coupled with calls to Attribute(), for example, in the following situation:
var siteNodes = from siteNode in
doc.Elements("SiteConfig")
.Elements("Sites")
.Elements("Site")
where siteNode.Attribute("Identifier").Value == "a"
select siteNode;
(That is, there's no equivalent to Attributes("xxx").Value)
Is there something built-in to the framework to handle this situation a little better? What I would prefer is a version of Element() (and of Attribute() while we're at it) that throws a descriptive exception (e.g. "Looking for element <xyz> under <abc> but no such element was found") instead of returning null.
I could write my own version of Element() and Attribute() but it just seems to me like this is such a common scenario that I must be missing something...
You could implement your desired functionality as an extension method:
public static class XElementExtension
{
public static XElement ElementOrThrow(this XElement container, XName name)
{
XElement result = container.Element(name);
if (result == null)
{
throw new InvalidDataException(string.Format(
"{0} does not contain an element {1}",
container.Name,
name));
}
return result;
}
}
You would need something similar for XDocument. Then use it like this:
var siteNodes = from siteNode in
doc.ElementOrThrow("SiteConfig")
.ElementOrThrow("SiteUrls")
.Elements("Sites")
select siteNode;
Then you will get an exception like this:
SiteConfig does not contain an element SiteUrls
You could use XPathSelectElements
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
class Program
{
static void Main()
{
var ids = from site in XDocument.Load("test.xml")
.XPathSelectElements("//SiteConfig/Sites/Site")
let id = site.Attribute("Identifier")
where id != null
select id;
foreach (var item in ids)
{
Console.WriteLine(item.Value);
}
}
}
Another thing that comes to mind is to define an XSD schema and validate your XML file against this schema. This will generate meaningful error messages and if the file is valid you can parse it without problems.

Resources