Giter Club home page Giter Club logo

plist-cil's Introduction

plist-cil - A C# library for working with property lists

Build Status

This library enables .NET (CLR) applications to handle property lists of various formats. It is mostly based on dd-plist for Java. And as it, this is licensed under the terms of the MIT license.

Property lists are files used to store user settings and serialized objects. They originate from the NeXTSTEP programming environment and are now a basic part of thhe Cocoa framework (macOS and iOS) as well as the GNUstep framework.

Features

  • Read / write property lists from / to files, streams or byte arrays
  • Convert between property lists formats
  • Property list contents are provided as objects from the NeXTSTEP environment (NSDictionary, NSArray, NSString, etc.)
  • Serialize native .NET data structures to property list objects
  • Deserialize from property list objects to native .NET data structures

Supported formats

  • Cocoa XML
  • Cocoa Binary (v0)
  • Cocoa / NeXTSTEP / GNUstep ASCII

Requirements

plist-cil targets:

  • .NET Framework 4.5,
  • .NET Standard 2.0
  • .NET Core 2.1
  • .NET Core 3.1.
  • .NET 5.0

This means it should be compatible with Mono, Xamarin.iOS, Xamarin.Mac, UWP, etc. If you find an incompatibility, please create an issue.

Download

The latest releases can be downloaded here.

NuGet support

You can download the NuGet package directly from the release page or from the NuGet Gallery or from here.

Help

The API documentation is included in the download.

When you encounter a bug please report it by on the issue tracker.

Usage examples

Reading

Parsing can be done with the PropertyListParser class. You can feed the PropertyListParser with a FileInfo, a Stream or a byte array. The Parse method of the PropertyListParser will parse the input and give you a NSObject as result. Generally this is a NSDictionary but it can also be a NSArray.

Note: Property lists created by NSKeyedArchiver are not yet supported.

You can then navigate the contents of the property lists using the various classes extending NSObject. These are modeled in such a way as to closely resemble the respective Cocoa classes.

You can also directly convert the contained NSObject objects into native .NET Objects with the NSOBject.ToObject() method. Using this method you can avoid working with NSObject instances altogether.

Writing

You can create your own property list using the various constructors of the different NSObject classes. Or you can wrap existing native .NET structures with the method NSObject.Wrap(Object o). Just make sure that the root object of the property list is either a NSDictionary (can be created from objects of the type Dictionary<string, Object>) or a NSArray (can be created from object arrays).

For building a XML property list you can then call the ToXml method on the root object of your property list. It will give you an UTF-8 string containing the property list in XML format.

If you want to have the property list in binary format use the BinaryPropertyListWriter class. It can write the binary property list directly to a file or to a Stream.

When you directly want to save your property list to a file, you can also use the SaveAsXml or SaveAsBinary methods of the PropertyListParser class.

Converting

For converting a file into another format there exist convenience methods in the PropertyListParser class: ConvertToXml, ConvertToBinary, ConvertToASCII and ConvertToGnuStepASCII.

Code snippets

Reading

try
{
	FileInfo file = new FileInfo("Info.plist");
	NSDictionary rootDict = (NSDictionary)PropertyListParser.Parse(file);
	string name = rootDict.ObjectForKey("Name").ToString();
	NSObject[] parameters = ((NSArray)rootDict.ObjectForKey("Parameters")).GetArray();
	foreach(NSObject param in parameters)
	{
		if(param.GetType().Equals(typeof(NSNumber)))
		{
			NSNumber num = (NSNumber)param;
			switch(num.GetNSNumberType())
			{
				case NSNumber.BOOLEAN:
				{
					bool boolean = num.ToBool();
					// ...
					break;
				}
				case NSNumber.INTEGER:
				{
					long l = num.ToLong();
					// or int i = num.ToInt();
					// ...
					break;
				}
				case NSNumber.REAL:
				{
					double d = num.ToDouble();
					// ...
					break;
				}
			}
		}
		// else...
	}
}
catch(Exception ex)
{
	Console.WriteLine(ex.StackTrace);
}

Writing

// Creating the root object
NSDictionary root = new NSDictionary();

// Creation of an array of length 2
NSArray people = new NSArray(2);

// Creation of the first object to be stored in the array
NSDictionary person1 = new NSDictionary();
// The NSDictionary will automatically wrap strings, numbers and dates in the respective NSObject subclasses
person1.Add("Name", "Peter"); // This will become a NSString
// Use the DateTime class
person1.Add("RegistrationDate", new DateTime(2011, 1, 13, 9, 28, 0)); // This will become a NSDate
person1.Add("Age", 23); // This will become a NSNumber
person1.Add("Photo", new NSData(new FileInfo("peter.jpg")));

// Creation of the second object to be stored in the array
NSDictionary person2 = new NSDictionary();
person2.Add("Name", "Lisa");
person2.Add("Age", 42);
person2.Add("RegistrationDate", new NSDate("2010-09-23T12:32:42Z"));
person2.Add("Photo", new NSData(new FileInfo("lisa.jpg")));

// Put the objects into the array
people.SetValue(0, person1);
people.SetValue(1, person2);

// Put the array into the property list
root.Add("People", people);

// Save the property list
PropertyListParser.SaveAsXml(root, new FileInfo("people.plist"));

plist-cil's People

Contributors

bash avatar claunia avatar qmfrederik avatar reima avatar tevo45 avatar wewewer123 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

plist-cil's Issues

Support for asynchronous IO in ASP.NET Core

First of all, great work on this library!

We're using it in an ASP.NET Core 3.x web application to parse the plist data in the request body, unfortunately as of 3.0 by default the server is configured to disallow synchronous IO operations.

This results in an InvalidOperationException from within the HttpRequestStream when attempting to copy it's contents to a MemoryStream.

System.InvalidOperationException: Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.Stream.CopyTo(Stream destination, Int32 bufferSize)
   at System.IO.Stream.CopyTo(Stream destination)
   at Claunia.PropertyList.PropertyListParser.ReadAll(Stream fs)
   at Claunia.PropertyList.PropertyListParser.Parse(Stream fs)

We are currently working around this by allowing synchronous IO but it seems that the preferred approach is to update the code & libraries to use async stream operations.

I realise this is likely a non-trivial change in the plist-cil code but curious to hear your thoughts about this.

SaveToXml adding Byte Order Mark

When using PropertyListParser.SaveAsXml(); the output is an xml with the Byte Order Mark (BOM) which are non ASCII characters.

I made a simple test of loading a binary Info.plist and the just save the same content but into an XML.

The result

vis ~/Desktop/temp.plist
\M-o\M-;\M-?<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

Instead of

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

This brings a lot of problem later, specially with fastlane.

A Fix

This method is using Encoding.UTF8

public static void SaveAsXml(NSObject root, Stream outStream)
{
    using var w = new StreamWriter(outStream, Encoding.UTF8, 1024, true);

    w.Write(root.ToXmlPropertyList());
}

It should be using

public static void SaveAsXml(NSObject root, Stream outStream)
{
    var utf8Encoding = new UTF8Encoding(false);
    using var w = new StreamWriter(outStream, utf8Encoding, 1024, true);

    w.Write(root.ToXmlPropertyList());
}

This is enough to save the xml in the correct form.

Error on .NET Core LTS

I am using the LTS version of .NET Core on macOS 10.12.2. My output of dotnet --version is 1.0.0-preview2-003156.

When I try to include version 1.14.0 in my project.json I get the following errors:

error: Package plist-cil 1.14.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package plist-cil 1.14.0 supports: net (.NETFramework,Version=v0.0)
error: One or more packages are incompatible with .NETCoreApp,Version=v1.0.

Here is the project.json that I used:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.3"
        },
	"plist-cil": "1.14.0"
      },
      "imports": "dnxcore50"
    }
  }
}

Binary plist parse failing due to apparent timestamp conversion

I'm experiencing an exception when attempting to parse a binary plist. The exception thrown is:

{"The added or subtracted value results in an un-representable DateTime.\r\nParameter name: value"}

Unfortunately, I cannot share the plist, but is there anyway to work around this?

Thanks

Example

Can you please post an example of how to use this library?

Better support for UIDs

plist-cil is one of the few .NET plist libraries that cna handle UIDs properly, congrats to that :-).

However, UIDs are essentially numbers, and it would be great if you could change the UID class so that:

  • It takes an integer as a parameter (not sure what the name is used for)
  • When serializing the integer value, it follows the same rule as for NSNumbers -- if the value is less than byte.MaxValue, only a single byte is used, if the value is less than ushort.MaxValue, to bytes are used, and so forth.

Cheers

Build instructions?

I tried to build my own NuGet with some modifications, but looks like target 'net45' fails to build.

Are there any reproducible build steps to generate a working NuGet package?

Updated release on NuGet

It looks like there hasn't been any release of plist-cil for a while. A couple of PRs got merged a while ago (especially #51).

Do you think you could publish a new release to NuGet?

Thanks!

Add support for .NET 5.0

.NET 5.0 was released in November 10, 2020, and many libraries started using it. Can you add support for this version of .NET?

[Xml Reading problem] A text after "CData section" is not enough string.

Hi,

I trying following xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key><![CDATA[b%]]]]><![CDATA[>def]]></key>
	<string><![CDATA[b%]]]]><![CDATA[>def]]></string>
</dict>
</plist>

I expected "b%]]>def" string and key.
But I got "b%]]"
It is strange.

Because, following CData parsing code is not enough.
https://github.com/claunia/plist-cil/blob/master/plist-cil/XmlPropertyListParser.cs#L201

So, I propose following implementation.

        static string GetNodeTextContents(XmlNode n)
        {
            if (n.NodeType == XmlNodeType.Text || n.NodeType == XmlNodeType.CDATA)
            {
                string content = n.Value; //This concatenates any adjacent text/cdata/entity nodes
                return content ?? "";
            }
            if (n.HasChildNodes)
            {
                XmlNodeList children = n.ChildNodes;

                string text = "";
                foreach (XmlNode child in children)
                {                 
                    //Skip any non-text nodes, like comments or entities
                    if (child.NodeType == XmlNodeType.Text || child.NodeType == XmlNodeType.CDATA)
                    {
                        if(child.Value != null) {
                            text += child.Value;
                        }
                    }
                }
                return text;
            }
            return "";
        }

But it is hard for me to construct testing environment this project... :(
How can I contribute ?

Save to XXX issue

    public static void SaveAsASCII(NSArray root, FileInfo outFile)
        {
            string parent = outFile.DirectoryName;
            if (!Directory.Exists(parent))
                Directory.CreateDirectory(parent);
            using (Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite))
            using (StreamWriter w = new StreamWriter(fous, Encoding.ASCII))
            {
                w.Write(root.ToASCIIPropertyList());
            }
        }

It is not a full overwrite in case writing to an existing file and leaves an invalid file.

Next version numbering

This is a RFC as some changes from @qmfrederik have been incompatible changes at API level.

Should next version be 2.0 or do we need more features for that?

Fails to load in Xamarin.Android.

See sample project: https://github.com/JunielKatarn/PlistDroid

Adding plist-cil as a dependency throws build errors due to missing reference to System.Memory.

Could not load assembly 'System.Memory' during startup registration.

Adding System.Memory (version >=4.5) NuGet package solves the build issue, but fails again at runtime (the application doesn't load) with this message:

F/monodroid-assembly( 9097): Could not load assembly 'System.Memory' during startup registration.

This may be related to Xamarin.Android still being based on the Mono framework.
Is this something that can be fixed by adding certain platform target frameworks to the plist-cil's build process?

Release new version (1.16?) to support .NET Standard.

Can these NuGet packages be updated to target more recent .NET platforms?

Trying to install 1.15 into a Xamarin.Forms project throws the following error:

Could not install package 'plist-cil 1.15.0'. You are trying to install this package into a project that targets '.NETPortable,Version=v4.5,Profile=Profile259', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

(Note, 1.14 works fine for the case above).

.NET Standard
Class libraries of this type can't install any version of plist-cil with the following error:

Package plist-cil 1.14.0 is not compatible with netstandard1.4 (.NETStandard,Version=v1.4). Package plist-cil 1.14.0 supports: net (.NETFramework,Version=v0.0)
One or more packages are incompatible with .NETStandard,Version=v1.4.
Package restore failed. Rolling back package changes for 'ClassLibrary1'.

Sort key-value pairs alphabetically by key name when serializing dictionaries to XML

I'd like to suggest that plist-cil will sort key-value pairs alphabetically when it serializes a dictionary to XML, in the same way that plutil writes processed plists on the Mac.

While this will have no effect whatsoever on the data when deserialized, it will help greatly in cases where files are compared or diffed with previous versions to detect changes.

Right now the package serializes the pairs in the order that the Dictionary class provides, which is undefined. This means that a file loaded and written-back right away multiple times can potentially be different from one write to the other.

I'm considering plist-cil for a project which will process the about 200 plist files on the ProfileManifests project on an ongoing basis, and it is crucial to me that file output will be consistent and reproducible.

I can make the changes myself, but I'd like to see if there is openness to the idea around here ๐Ÿ™‚

Binary plist from Monterey beta is failing to parse

I've just tried reading a binary plist from the OSX Monterey beta, and the parse operation is failing:

Failed to read plist data - Array dimensions exceeded supported range. - at Claunia.PropertyList.BinaryPropertyListParser.DoParse(ReadOnlySpan1 bytes)
at Claunia.PropertyList.BinaryPropertyListParser.Parse(Byte[] data)
at Claunia.PropertyList.PropertyListParser.Parse(Byte[] bytes)
at myCallingCode`

Unfortunately, I cannot include the data as it contains personal information.

So I was just wondering if you were aware of any issues with binary plists on Monterey?

Roundtrip of the value 0 for reals

It turns out you can have multiple representations of the value 0 for reals. Until now, I always saw 0.0, but you can also have 0.000000 like this:

<key>TimeZoneOffsetFromUTC</key>
<real>0.000000</real>

If we want to roundtrip, we should keep the number of significant digits somewhere in the NSNumber object.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.