Converting plist to binary plist

220

Solution 1

Yes. All the plist code is part of CoreFoundation, which is opensource. CoreFoundation can be directly built and run on Linux and Windows, so you can write a CF tool using the normal APIs you would use on Mac OS X, but build and run it on other platforms.

The particular API you want to be looking at is CFPropertyListWriteToStream(). The code for CoreFoundation is available from Apple (tarball), among other places.

Finally depending on how often you update the file, how much processor you have to spare on the server, and how much repetition there is your data there may be one significant enhancement left that you can do. By default certain elements in binary plists are uniqued (such as strings). Other elements are not (such as arrays and dictionarts). The binary plist format allows them to be uniqued, the issue is that it is expensive to actually walk through and unique arrays and dictionaries. If you have a lot of identical arrays or dicts in your content you may see a significant size reduction by uniquing them. You can enable that by hacking up _flattenPlist() in CFBinaryPlist.c.

If you do that make sure to test it very thoroughly, and do not do on any files you cannot update over the network, just in case a future release makes any optimizations that break that. Also, make sure you are ready to turn it off at a moments notice.

Solution 2

There is a PHP and ruby implementation for that:

http://code.google.com/p/cfpropertylist/

Solution 3

The linked Ruby implementation is Ruby 1.9 only. I knocked up a quick binary serializer which works in Ruby 1.8.

http://gist.github.com/303378

Solution 4

It's not clear if you want to do the conversion on the iPhone or on the server. If it's on the server and you can use the Cocoa frameworks, the NSPropertyListSerialization provides services to convert between the supported plist types (string, XML, and binary) on OS X (since 10.2). There are also analogous methods in the Core Foundation library if you'd prefer to use that instead.

To convert an XML plist to a binary one:

NSString *xmlPlistPath; // already set
NSString *outPath; // already set


NSData *plistData;
NSString *error;
NSPropertyListFormat format;
id plist;
plistData = [NSData dataWithContentsOfFile:xmlPlistPath];

plist = [NSPropertyListSerialization propertyListFromData:plistData
                                         mutabilityOption:NSPropertyListImmutable
                                                   format:&format
                                         errorDescription:&error];

if(plist == nil) { // unable to parse plist
    //deal with failure -- error gives description of the error
} else {
    binaryPlistData = [NSPropertyListSerialization dataFromPropertyList:plist
                                                                 format:NSPropertyListBinaryFormat_v1_0
                                                       errorDescription:&error];
    if(binaryPlistData == nil) {//unable to create serialized plist
         // deal with failure -- error gives description of the error
    }

    if(![binaryPlistData writeToFile:outPath atomically:YES]) {
        // unable to write file
    }
}

See Property List Pramming Guide page on developer.apple.com for more information.

Solution 5

Command-line tool plutil - property list utility

Apple has two very good command-line tools for mangling property list files.

  • /usr/libexec/Plistbuddy - for editing plists
  • /usr/bin/plutil - syntax checking and type conversion

From plutil man page:

plutil can be used to check the syntax of property list files, or convert a plist file from one format to another. Specifying - as an input file reads from stdin.

Converting an existing plist to XML, Binary, or JSON format

plutil -convert xml1 stops2.plist
plutil -convert binary1 stops2.plist
plutil -convert json stops2.plist
Share:
220
Mayur Kulkarni
Author by

Mayur Kulkarni

Updated on June 06, 2022

Comments

  • Mayur Kulkarni
    Mayur Kulkarni almost 2 years

    I have a Activity in which when i click on refresh action bar icon it starts an async task. I have callbacks of async task in mainactity. in preexecute i am displaying progressbar on action bar and in postexecute i am displaying back refresh item. But when there is screen rotation after async task is started, onpostexecute runs but it is not calling invalidate options menu.

    please help me out.

    here is my MainActivity:

    package in.cdac.enbee;
    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.Toast;
    
    public class MainActivity extends Activity implements RefreshTask.TaskCallbacks {
    
    	// Our created menu to use
        private Menu mymenu;
    	volatile static boolean isrefreshing=false;
    	
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
        	Log.d("Debug", "OncreateOptionsMenu");
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            
            // We should save our menu so we can use it to reset our updater.
            mymenu = menu;
            
            if(isrefreshing) {
            	 menu.findItem(R.id.action_refresh).setActionView(R.layout.action_progressbar);
    		} else {
    			menu.findItem(R.id.action_refresh).setActionView(null);;
    		}
            return true;
        }
        
       
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
        	
            switch(item.getItemId()) {
            case R.id.action_refresh:
            	// Do animation start
                new RefreshTask(this).execute();
                return true;
            case R.id.action_settings:
            	return true;
            }
            
            return super.onOptionsItemSelected(item);
        }
        
        
        @Override
        protected void onSaveInstanceState (Bundle savedInstanceState) {
        	//Log.d("Debug", "OnSave");
        	//Always call superclass first
        	super.onSaveInstanceState(savedInstanceState);
        	// Restore value of members from saved state
            //savedInstanceState.putBoolean("isRefreshing", isrefreshing);
            
        }
        
        @Override
        protected void onRestoreInstanceState (Bundle savedInstanceState) {
        	//Log.d("Debug", "OnRestore");
        	// Always call the superclass so it can restore the view hierarchy
            super.onRestoreInstanceState(savedInstanceState);
            if (savedInstanceState != null) {
                // Restore value of members from saved state
            	//isrefreshing = savedInstanceState.getBoolean("isRefreshing");
            } else {
                // Probably initialize members with default values for a new instance
            	//isrefreshing = false;
            }
               
        }   
        
    	@Override
    	public void onPreExecute() {
    		// TODO Auto-generated method stub
    		isrefreshing = true;
    		invalidateOptionsMenu();
    	}
    
    
    	@Override
    	public void onCancelled() {
    		// TODO Auto-generated method stub
    		isrefreshing = false;
    		invalidateOptionsMenu();
    	}
    
    
    	@Override
    	public void onPostExecute(Boolean done) {
    		// TODO Auto-generated method stub
       		isrefreshing = false;
    		
    		if(done) {
        		Toast.makeText(this, "Done refreshing", Toast.LENGTH_SHORT).show();
        	} else {
        		Toast.makeText(this, "Downloading Failed", Toast.LENGTH_SHORT).show();
        	}
    		invalidateOptionsMenu();
    	}
        
    }

    and my async task:

    package in.cdac.enbee;
    
    import android.os.AsyncTask;
    
    public class RefreshTask extends AsyncTask<Void, Void, Boolean> {
    	 
        /**
    	 * 
    	 */
        
        static interface TaskCallbacks {
    		void onPreExecute();
    		void onCancelled();
    		void onPostExecute(Boolean done);
    	}
        
        private TaskCallbacks mCallbacks;
          
        public RefreshTask(TaskCallbacks mCallbacks) {
    		this.mCallbacks = mCallbacks;
    	}
        
        @Override
    	protected void onPreExecute() {
    		mCallbacks.onPreExecute();
    		
    	}
    
        @Override
        protected void onCancelled() {
          if (mCallbacks != null) {
            mCallbacks.onCancelled();
          }
        }
        
        @Override
        protected Boolean doInBackground(Void... nope) {
            try {
                // Set a time to simulate a long update process.
                Thread.sleep(4000);
                 
                return true;
                 
            } catch (Exception e) {
                return false;
            }
        }
         
        
        @Override
    	protected void onPostExecute(Boolean done) {
    		if (mCallbacks != null) {
    			mCallbacks.onPostExecute(done);
    		}
    	}
    
    }

    and my layout file for progressbar:

    <?xml version="1.0" encoding="utf-8"?>
    <ProgressBar	xmlns:android="http://schemas.android.com/apk/res/android"
            		android:id="@+id/progressBar"
            		android:layout_width="wrap_content"
         			android:layout_height="wrap_content">
    </ProgressBar>

    Thank you.

    • Mayur Kulkarni
      Mayur Kulkarni over 9 years
      @Okas Can you tell me which part of code?
    • Okas
      Okas over 9 years
      Sorry, i misread your code.
  • Brian Cline
    Brian Cline over 15 years
    Thanks, this helps a lot. I'll probably be building this and wrapping some of these functions into a PHP module.
  • Yann Biancheri
    Yann Biancheri almost 14 years
    I was just looking for how to do that to cache data locally. Thanks!
  • Sam Soffes
    Sam Soffes almost 14 years
    This is really awesome. Exactly what I needed. Thanks so much!
  • Henry
    Henry almost 14 years
    thx, but any Java implementation of CFPropertyListWriteToStrean()? :S
  • ckruse
    ckruse almost 13 years
    If you mean github.com/ckruse/CFPropertyList by „the linked Ruby implementation,“ then I have to say that this is not true. It is backward compatible to Ruby 1.8.
  • Okas
    Okas over 9 years
    It seems like part of your code is missing.
  • Mayur Kulkarni
    Mayur Kulkarni over 9 years
    So what do you suggest that i should do?
  • Franklin Yu
    Franklin Yu over 5 years
    Links are dead. New link for CFPropertyListWriteToStream() is here; it says that we should be using CFPropertyListWrite instead.