/// <summary> /// Adds a resource to the resources. If the resource is a string, /// it will be saved that way, otherwise it will be serialized /// and stored as in binary. /// </summary> private void AddDataRow(string elementName, string name, object value) { Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, " resx: adding resource " + name); switch (value) { case string str: AddDataRow(elementName, name, str); break; case byte[] bytes: AddDataRow(elementName, name, bytes); break; case ResXFileRef fileRef: { ResXDataNode node = new ResXDataNode(name, fileRef, typeNameConverter); DataNodeInfo info = node.GetDataNodeInfo(); AddDataRow(elementName, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment); break; } default: { ResXDataNode node = new ResXDataNode(name, value, typeNameConverter); DataNodeInfo info = node.GetDataNodeInfo(); AddDataRow(elementName, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment); break; } } }
internal DataNodeInfo GetDataNodeInfo() { bool shouldSerialize = true; if (nodeInfo != null) { shouldSerialize = false; } else { nodeInfo = new DataNodeInfo(); } nodeInfo.Name = Name; nodeInfo.Comment = Comment; // We always serialize if this node represents a FileRef. This is because FileRef is a public property, // so someone could have modified it. if (shouldSerialize || FileRefFullPath != null) { // if we dont have a datanodeinfo it could be either // a direct object OR a fileref if (FileRefFullPath != null) { nodeInfo.ValueData = FileRef.ToString(); nodeInfo.MimeType = null; nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXFileRef), typeNameConverter); } else { // serialize to string inside the nodeInfo FillDataNodeInfoFromObject(nodeInfo, value); } } return(nodeInfo); }
/// <summary> /// Adds a string resource to the resources. /// </summary> public void AddResource(ResXDataNode node) { // we're modifying the node as we're adding it to the resxwriter // this is BAD, so we clone it. adding it to a writer doesnt change it // we're messing with a copy ResXDataNode nodeClone = node.DeepClone(); ResXFileRef fileRef = nodeClone.FileRef; string modifiedBasePath = BasePath; if (!string.IsNullOrEmpty(modifiedBasePath)) { if (!modifiedBasePath.EndsWith("\\")) { modifiedBasePath += "\\"; } fileRef?.MakeFilePathRelative(modifiedBasePath); } DataNodeInfo info = nodeClone.GetDataNodeInfo(); AddDataRow(DataStr, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment); }
private void ParseDataNode(XmlTextReader reader, bool isMetaData) { DataNodeInfo nodeInfo = new DataNodeInfo { Name = reader[ResXResourceWriter.NameStr] }; string typeName = reader[ResXResourceWriter.TypeStr]; string alias = null; AssemblyName assemblyName = null; if (!string.IsNullOrEmpty(typeName)) { alias = GetAliasFromTypeName(typeName); } if (!string.IsNullOrEmpty(alias)) { assemblyName = aliasResolver.ResolveAlias(alias); } if (assemblyName != null) { nodeInfo.TypeName = GetTypeFromTypeName(typeName) + ", " + assemblyName.FullName; } else { nodeInfo.TypeName = reader[ResXResourceWriter.TypeStr]; } nodeInfo.MimeType = reader[ResXResourceWriter.MimeTypeStr]; bool finishedReadingDataNode = false; nodeInfo.ReaderPosition = GetPosition(reader); while (!finishedReadingDataNode && reader.Read()) { if (reader.NodeType == XmlNodeType.EndElement && (reader.LocalName.Equals(ResXResourceWriter.DataStr) || reader.LocalName.Equals(ResXResourceWriter.MetadataStr))) { // we just found </data>, quit or </metadata> finishedReadingDataNode = true; } else { // could be a <value> or a <comment> if (reader.NodeType == XmlNodeType.Element) { if (reader.Name.Equals(ResXResourceWriter.ValueStr)) { WhitespaceHandling oldValue = reader.WhitespaceHandling; try { // based on the documentation at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemxmlxmltextreaderclasswhitespacehandlingtopic.asp // this is ok because: // "Because the XmlTextReader does not have DTD information available to it, // SignificantWhitepsace nodes are only returned within the an xml:space='preserve' scope." // the xml:space would not be present for anything else than string and char (see ResXResourceWriter) // so this would not cause any breaking change while reading data from Everett (we never outputed // xml:space then) or from whidbey that is not specifically either a string or a char. // However please note that manually editing a resx file in Everett and in Whidbey because of the addition // of xml:space=preserve might have different consequences... reader.WhitespaceHandling = WhitespaceHandling.Significant; nodeInfo.ValueData = reader.ReadString(); } finally { reader.WhitespaceHandling = oldValue; } } else if (reader.Name.Equals(ResXResourceWriter.CommentStr)) { nodeInfo.Comment = reader.ReadString(); } } else { // weird, no <xxxx> tag, just the inside of <data> as text nodeInfo.ValueData = reader.Value.Trim(); } } } if (nodeInfo.Name == null) { throw new ArgumentException(string.Format(SR.InvalidResXResourceNoName, nodeInfo.ValueData)); } ResXDataNode dataNode = new ResXDataNode(nodeInfo, BasePath); if (UseResXDataNodes) { resData[nodeInfo.Name] = dataNode; } else { IDictionary data = (isMetaData ? resMetadata : resData); if (assemblyNames == null) { data[nodeInfo.Name] = dataNode.GetValue(typeResolver); } else { data[nodeInfo.Name] = dataNode.GetValue(assemblyNames); } } }
private object GenerateObjectFromDataNodeInfo(DataNodeInfo dataNodeInfo, ITypeResolutionService typeResolver) { object result = null; string mimeTypeName = dataNodeInfo.MimeType; // default behavior: if we dont have a type name, it's a string string typeName = string.IsNullOrEmpty(dataNodeInfo.TypeName) ? MultitargetUtil.GetAssemblyQualifiedName(typeof(string), typeNameConverter) : dataNodeInfo.TypeName; if (!string.IsNullOrEmpty(mimeTypeName)) { if (string.Equals(mimeTypeName, ResXResourceWriter.BinSerializedObjectMimeType)) { string text = dataNodeInfo.ValueData; byte[] serializedData = FromBase64WrappedString(text); if (binaryFormatter == null) { binaryFormatter = new BinaryFormatter { Binder = new ResXSerializationBinder(typeResolver) }; } IFormatter formatter = binaryFormatter; if (serializedData != null && serializedData.Length > 0) { result = formatter.Deserialize(new MemoryStream(serializedData)); if (result is ResXNullRef) { result = null; } } } else if (string.Equals(mimeTypeName, ResXResourceWriter.ByteArraySerializedObjectMimeType)) { if (!string.IsNullOrEmpty(typeName)) { Type type = ResolveType(typeName, typeResolver); if (type != null) { TypeConverter tc = TypeDescriptor.GetConverter(type); if (tc.CanConvertFrom(typeof(byte[]))) { string text = dataNodeInfo.ValueData; byte[] serializedData = FromBase64WrappedString(text); if (serializedData != null) { result = tc.ConvertFrom(serializedData); } } } else { string newMessage = string.Format(SR.TypeLoadException, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); XmlException xml = new XmlException(newMessage, null, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); TypeLoadException newTle = new TypeLoadException(newMessage, xml); throw newTle; } } } } else if (!string.IsNullOrEmpty(typeName)) { Type type = ResolveType(typeName, typeResolver); if (type != null) { if (type == typeof(ResXNullRef)) { result = null; } else if (typeName.IndexOf("System.Byte[]") != -1 && typeName.IndexOf("mscorlib") != -1) { // Handle byte[]'s, which are stored as base-64 encoded strings. // We can't hard-code byte[] type name due to version number // updates & potential whitespace issues with ResX files. result = FromBase64WrappedString(dataNodeInfo.ValueData); } else { TypeConverter tc = TypeDescriptor.GetConverter(type); if (tc.CanConvertFrom(typeof(string))) { string text = dataNodeInfo.ValueData; try { result = tc.ConvertFromInvariantString(text); } catch (NotSupportedException nse) { string newMessage = string.Format(SR.NotSupported, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X, nse.Message); XmlException xml = new XmlException(newMessage, nse, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); NotSupportedException newNse = new NotSupportedException(newMessage, xml); throw newNse; } } else { Debug.WriteLine("Converter for " + type.FullName + " doesn't support string conversion"); } } } else { string newMessage = string.Format(SR.TypeLoadException, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); XmlException xml = new XmlException(newMessage, null, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); TypeLoadException newTle = new TypeLoadException(newMessage, xml); throw newTle; } } else { // if mimeTypeName and typeName are not filled in, the value must be a string Debug.Assert(value is string, "Resource entries with no Type or MimeType must be encoded as strings"); } return(result); }
private void FillDataNodeInfoFromObject(DataNodeInfo nodeInfo, object value) { if (value is CultureInfo ci) { // special-case CultureInfo, cannot use CultureInfoConverter for serialization nodeInfo.ValueData = ci.Name; nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(CultureInfo), typeNameConverter); } else if (value is string str) { nodeInfo.ValueData = str; } else if (value is byte[] bytes) { nodeInfo.ValueData = ToBase64WrappedString(bytes); nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(byte[]), typeNameConverter); } else { Type valueType = (value == null) ? typeof(object) : value.GetType(); if (value != null && !valueType.IsSerializable) { throw new InvalidOperationException(string.Format(SR.NotSerializableType, name, valueType.FullName)); } TypeConverter tc = TypeDescriptor.GetConverter(valueType); bool toString = tc.CanConvertTo(typeof(string)); bool fromString = tc.CanConvertFrom(typeof(string)); try { if (toString && fromString) { nodeInfo.ValueData = tc.ConvertToInvariantString(value); nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, typeNameConverter); return; } } catch (Exception ex) { // Some custom type converters will throw in ConvertTo(string) // to indicate that this object should be serialized through ISeriazable // instead of as a string. This is semi-wrong, but something we will have to // live with to allow user created Cursors to be serializable. if (ClientUtils.IsSecurityOrCriticalException(ex)) { throw; } } bool toByteArray = tc.CanConvertTo(typeof(byte[])); bool fromByteArray = tc.CanConvertFrom(typeof(byte[])); if (toByteArray && fromByteArray) { byte[] data = (byte[])tc.ConvertTo(value, typeof(byte[])); nodeInfo.ValueData = ToBase64WrappedString(data); nodeInfo.MimeType = ResXResourceWriter.ByteArraySerializedObjectMimeType; nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, typeNameConverter); return; } if (value == null) { nodeInfo.ValueData = string.Empty; nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), typeNameConverter); } else { if (binaryFormatter == null) { binaryFormatter = new BinaryFormatter { Binder = new ResXSerializationBinder(typeNameConverter) }; } using (MemoryStream ms = new MemoryStream()) { binaryFormatter.Serialize(ms, value); nodeInfo.ValueData = ToBase64WrappedString(ms.ToArray()); } nodeInfo.MimeType = ResXResourceWriter.DefaultSerializedObjectMimeType; } } }
internal ResXDataNode(DataNodeInfo nodeInfo, string basePath) { this.nodeInfo = nodeInfo; InitializeDataNode(basePath); }