From 8de0b897089786c42b0fb4a58c4110aebc5960ee Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Tue, 3 Jun 2025 18:52:07 +0200 Subject: [PATCH 1/9] Add dictionary for maps --- nemo-physical/src/datavalues/any_datavalue.rs | 2 + nemo-physical/src/datavalues/datavalue.rs | 21 ++ nemo-physical/src/datavalues/map_datavalue.rs | 30 ++ nemo-physical/src/dictionary.rs | 1 + nemo-physical/src/dictionary/map_dv_dict.rs | 338 ++++++++++++++++++ nemo-physical/src/dictionary/meta_dv_dict.rs | 73 +++- nemo-physical/src/dictionary/tuple_dv_dict.rs | 38 +- nemo-physical/src/lib.rs | 1 + 8 files changed, 484 insertions(+), 20 deletions(-) create mode 100644 nemo-physical/src/dictionary/map_dv_dict.rs diff --git a/nemo-physical/src/datavalues/any_datavalue.rs b/nemo-physical/src/datavalues/any_datavalue.rs index 3899c4748..31c7adc5b 100644 --- a/nemo-physical/src/datavalues/any_datavalue.rs +++ b/nemo-physical/src/datavalues/any_datavalue.rs @@ -627,7 +627,9 @@ impl DataValue for AnyDataValue { fn length(&self) -> Option; fn len_unchecked(&self) -> usize; fn tuple_element_unchecked(&self, index: usize) -> &AnyDataValue; + fn tuple_len_unchecked(&self) -> usize; fn map_keys(&self) -> Option + '_>>; + fn map_items(&self) -> Option + '_>>; fn map_element_unchecked(&self, key: &AnyDataValue) -> &AnyDataValue; } } diff --git a/nemo-physical/src/datavalues/datavalue.rs b/nemo-physical/src/datavalues/datavalue.rs index 3f6aa69e9..ed48f54e8 100644 --- a/nemo-physical/src/datavalues/datavalue.rs +++ b/nemo-physical/src/datavalues/datavalue.rs @@ -543,6 +543,20 @@ pub trait DataValue: Debug + Display + Into + PartialEq + Eq + Has panic!("Value is not a tuple"); } + /// Return the length of this complex value in tuple form. For + /// values- in the domain [ValueDomain::Tuple], this is the same + /// as [len_unchecked]. For values in the domain + /// [ValueDomain::Map], this is the length of the tuple encoding, + /// i.e., twice the value of [len_unchecked] (which counts + /// key–value pairs). + /// + /// # Panics + /// Panics if the value is not a collection + #[must_use] + fn tuple_len_unchecked(&self) -> usize { + self.len_unchecked() + } + /// Returns an iterator over all keys in a value that is a map. /// None is returned for values that are no maps. #[must_use] @@ -550,6 +564,13 @@ pub trait DataValue: Debug + Display + Into + PartialEq + Eq + Has None } + /// Returns an iterator over all key–value pairs in a value that is a map. + /// None is returned for values that are no maps. + #[must_use] + fn map_items(&self) -> Option + '_>> { + None + } + /// Returns true if the value is a map that contains the given value /// as a key. Otherwise, false is returned. #[must_use] diff --git a/nemo-physical/src/datavalues/map_datavalue.rs b/nemo-physical/src/datavalues/map_datavalue.rs index b35543627..ad72799c6 100644 --- a/nemo-physical/src/datavalues/map_datavalue.rs +++ b/nemo-physical/src/datavalues/map_datavalue.rs @@ -32,6 +32,18 @@ impl MapDataValue { pairs: pairs_iter.into_iter().collect(), } } + + pub(crate) fn from_chunks<'a, T: Iterator + 'a>( + label: Option, + chunks: T, + ) -> Self { + Self { + label, + pairs: chunks + .map(|item| (item[0].clone(), item[1].clone())) + .collect(), + } + } } impl FromIterator<(AnyDataValue, AnyDataValue)> for MapDataValue { @@ -89,6 +101,10 @@ impl DataValue for MapDataValue { Some(Box::new(self.pairs.keys())) } + fn map_items(&self) -> Option + '_>> { + Some(Box::new(self.pairs.iter())) + } + fn contains(&self, key: &AnyDataValue) -> bool { self.pairs.contains_key(key) } @@ -100,6 +116,20 @@ impl DataValue for MapDataValue { fn map_element_unchecked(&self, key: &AnyDataValue) -> &AnyDataValue { self.pairs.get(key).expect("unchecked method") } + + fn tuple_element_unchecked(&self, index: usize) -> &AnyDataValue { + let pair = index / 2; + let (key, value) = self.pairs.iter().nth(pair).expect("unchecked method"); + + match index % 2 == 0 { + true => key, + false => value, + } + } + + fn tuple_len_unchecked(&self) -> usize { + 2 * self.pairs.len() + } } impl std::hash::Hash for MapDataValue { diff --git a/nemo-physical/src/dictionary.rs b/nemo-physical/src/dictionary.rs index 456ddfcbe..f1dd9499a 100644 --- a/nemo-physical/src/dictionary.rs +++ b/nemo-physical/src/dictionary.rs @@ -30,6 +30,7 @@ pub(crate) mod string_langstring_dv_dict; pub(crate) mod null_dv_dict; pub(crate) use null_dv_dict::NullDvDictionary; +pub(crate) mod map_dv_dict; pub(crate) mod tuple_dv_dict; pub mod meta_dv_dict; diff --git a/nemo-physical/src/dictionary/map_dv_dict.rs b/nemo-physical/src/dictionary/map_dv_dict.rs new file mode 100644 index 000000000..b17be45f5 --- /dev/null +++ b/nemo-physical/src/dictionary/map_dv_dict.rs @@ -0,0 +1,338 @@ +//! A [DvDict] implementation that manages tuple datavalues. + +use crate::datavalues::{AnyDataValue, IriDataValue, MapDataValue, NullDataValue}; +use crate::dictionary::tuple_dv_dict::{ + TUPLE_TYPE_BOOL, TUPLE_TYPE_DICTID32, TUPLE_TYPE_DICTID64, TUPLE_TYPE_F32, TUPLE_TYPE_F64, + TUPLE_TYPE_I64, TUPLE_TYPE_LABELID32, TUPLE_TYPE_LABELID64, TUPLE_TYPE_NOTYPE, + TUPLE_TYPE_NULLID32, TUPLE_TYPE_NULLID64, TUPLE_TYPE_U64, +}; +use crate::management::bytesized::ByteSized; + +use super::tuple_dv_dict::{tuple_bytes, tuple_bytes_mut}; +use super::{ + AddResult, DvDict, meta_dv_dict::MetaDvDictionary, tuple_dv_dict::TupleBytesPairDictionary, +}; + +/// Dictionary for storing IDs of Map. This is not a subdictionary like the others, since +/// we need mut access to the parent [MetaDvDictionary] to recursively create IDs for subterms +/// of a Map. +#[derive(Debug, Default)] +pub(crate) struct MapDvDict { + /// Internal dictionary to store map term types and parameter + /// values. This reuses the encoding for tuples, treating the Map + /// as a tuple alternating between keys and values. + dict: TupleBytesPairDictionary, +} + +impl MapDvDict { + /// Construct a new and empty dictionary. + pub(crate) fn new() -> Self { + Self::default() + } +} + +impl DvDict for MapDvDict { + /// This method always rejects. + fn add_datavalue(&mut self, _dv: AnyDataValue) -> AddResult { + AddResult::Rejected + } + + fn add_datavalue_with_parent_fn( + &self, + ) -> fn(&mut MetaDvDictionary, dict_id: usize, dv: AnyDataValue) -> AddResult { + add_map_datavalue_with_parent + } + + fn fresh_null(&mut self) -> (AnyDataValue, usize) { + unimplemented!("map dictionaries cannot make fresh nulls"); + } + + fn fresh_null_id(&mut self) -> usize { + unimplemented!("map dictionaries cannot make fresh nulls"); + } + + fn datavalue_to_id(&self, _dv: &AnyDataValue) -> Option { + unimplemented!( + "map dictionaries do not support local `datavalue_to_id`; use the callback `datavalue_to_id_with_parent_fn`" + ); + } + + fn datavalue_to_id_with_parent_fn( + &self, + ) -> fn(&MetaDvDictionary, dict_id: usize, dv: &AnyDataValue) -> Option { + map_datavalue_to_id_with_parent + } + + fn id_to_datavalue(&self, _id: usize) -> Option { + unimplemented!( + "map dictionaries do not support local `id_to_datavalue`; use the callback `id_to_datavalue_with_parent_fn`" + ); + } + + fn id_to_datavalue_with_parent_fn( + &self, + ) -> fn(&MetaDvDictionary, dict_id: usize, id: usize) -> Option { + id_to_map_datavalue_with_parent + } + + fn len(&self) -> usize { + self.dict.len() + } + + fn is_iri(&self, _id: usize) -> bool { + false + } + + fn is_plain_string(&self, _id: usize) -> bool { + false + } + + fn is_lang_string(&self, _id: usize) -> bool { + false + } + + fn is_null(&self, _id: usize) -> bool { + false + } + + /// This method always rejects. + fn mark_dv(&mut self, _dv: AnyDataValue) -> AddResult { + AddResult::Rejected + } + + fn has_marked(&self) -> bool { + self.dict.has_marked() + } +} + +impl ByteSized for MapDvDict { + fn size_bytes(&self) -> u64 { + let size = self.dict.size_bytes(); + log::debug!( + "MapDvDictionary with {} entries: {} bytes", + self.len(), + size + ); + size + } +} + +/// Adds a tuple value to the dictionary. Subvalues that are used inside the tuple are +/// added to the dictionary recursively, if necessary. The same applies to the label, if any. +/// +/// Following the general callback scheme of [DvDict::add_datavalue_with_parent_fn], the +/// function fetches the "self" tuple dict from its parent based on the given dictionary id. +fn add_map_datavalue_with_parent( + parent_dict: &mut MetaDvDictionary, + dict_id: usize, + dv: AnyDataValue, +) -> AddResult { + let (tuple_type, tuple_content) = tuple_bytes_mut(parent_dict, dv); + + let myself = parent_dict.sub_dictionary_mut_unchecked(dict_id); + if let Some(map_dict) = myself.as_any_mut().downcast_mut::() { + map_dict.dict.add_pair(&tuple_type, &tuple_content) + } else { + panic!( + "map dictionary called with a subdictionary id that does not belong to a tuple dict; this is a bug" + ) + } +} + +/// Converts a tuple value to a dictionary id, if that tuple is known to the dictionary. +/// Identifiers of subvalues that are used inside the tuple resolved recursively, if necessary. +/// The same applies to the label, if any. +/// +/// Following the general callback scheme of [DvDict::datavalue_to_id_with_parent_fn], the +/// function fetches the "self" tuple dict from its parent based on the given dictionary id. +fn map_datavalue_to_id_with_parent( + parent_dict: &MetaDvDictionary, + dict_id: usize, + dv: &AnyDataValue, +) -> Option { + let (tuple_type, tuple_content) = tuple_bytes(parent_dict, dv)?; + + let myself = parent_dict.sub_dictionary_unchecked(dict_id); + if let Some(map_dict) = myself.as_any().downcast_ref::() { + map_dict.dict.pair_to_id(&tuple_type, &tuple_content) + } else { + panic!( + "map dictionary called with a subdictionary id that does not belong to a tuple dict; this is a bug" + ) + } +} + +/// Converts a dictionary id to a tuple value, if that id is known to the tuple value dictionary. +/// Datavalues of subvalues that are used inside the recovered recursively, if necessary. It is +/// an error if any of the subvalues used in the tuple do not have a dictionary entry. +/// +/// Following the general callback scheme of [DvDict::datavalue_to_id_with_parent_fn], the +/// function fetches the "self" tuple dict from its parent based on the given dictionary id. +/// +/// # Panics +/// If the label or any of the dictinary-managed subvalues of this tuple cannot be found in the +/// dictionary. Also, the function will panic if the given `dict_id` is not affiliated with a +/// [TupleDvDict] in the given `parent_dict`. +fn id_to_map_datavalue_with_parent( + parent_dict: &MetaDvDictionary, + dict_id: usize, + id: usize, +) -> Option { + /// Macro for copying some bytes from a byte array while advancing a position counter. + macro_rules! get_value_bytes { + ($number:literal, $source_pos: ident, $source: ident, $bytes:ident) => { + let mut $bytes: [u8; $number] = [0; $number]; + $bytes.copy_from_slice(&$source[$source_pos..=$source_pos + $number - 1]); + $source_pos += $number; + }; + } + + let tuple_type: Vec; + let tuple_content: Vec; + let myself = parent_dict.sub_dictionary_unchecked(dict_id); + if let Some(tuple_dict) = myself.as_any().downcast_ref::() { + (tuple_type, tuple_content) = tuple_dict.dict.id_to_pair(id)?; + } else { + panic!( + "map dictionary called with a subdictionary id that does not belong to a tuple dict; this is a bug" + ) + } + + // read label, if any + let label: Option; + let mut pos_types: usize = 0; + // Note: the case is_empty() can occur when storing an empty tuple that has no label + if !tuple_type.is_empty() && tuple_type[0] == TUPLE_TYPE_LABELID32 { + pos_types += 1; // consume initial type byte + get_value_bytes!(4, pos_types, tuple_type, label_bytes); + if let Ok(label_id) = usize::try_from(u32::from_be_bytes(label_bytes)) { + label = Some(parent_dict.id_to_datavalue(label_id).expect( + "failed to find specified label of a tuple value; dictionaries seem to be corrupted", + ).try_into().expect("label recovered from dictionary is not an IRI; this is a bug")); + } else { + panic!( + "failed to convert u32 to usize; platforms with less than 32bit are not supported" + ); + } + } else if !tuple_type.is_empty() && tuple_type[0] == TUPLE_TYPE_LABELID64 { + pos_types += 1; // consume initial type byte + get_value_bytes!(8, pos_types, tuple_type, label_bytes); + if let Ok(label_id) = usize::try_from(u64::from_be_bytes(label_bytes)) { + label = Some(parent_dict.id_to_datavalue(label_id).expect( + "failed to find specified label of a tuple value; dictionaries seem to be corrupted", + ).try_into().expect("label recovered from dictionary is not an IRI; this is a bug")); + } else { + panic!( + "failed to convert u64 to usize; dictionary surpasses the capabilities of this platform" + ); + } + pos_types += 9; + } else { + label = None; + } + + // read values + let mut pos_content: usize = 0; + let mut values: Vec = vec![]; + + let mut cur_type: u8 = TUPLE_TYPE_NOTYPE; + while pos_content < tuple_content.len() { + // automatically keeps last type when reaching end of type list before end of content: + if pos_types < tuple_type.len() { + cur_type = tuple_type[pos_types]; + pos_types += 1; + } + match cur_type { + TUPLE_TYPE_DICTID64 => { + get_value_bytes!(8, pos_content, tuple_content, value_bytes); + let id = u64::from_be_bytes(value_bytes); + if let Ok(u_id) = usize::try_from(id) { + values.push( + parent_dict + .id_to_datavalue(u_id) + .expect("value used within tuple not found in dictionary"), + ); + } else { + panic!( + "failed to convert u64 to usize; dictionary surpasses the capabilities of this platform" + ); + } + } + TUPLE_TYPE_DICTID32 => { + get_value_bytes!(4, pos_content, tuple_content, value_bytes); + let id = u32::from_be_bytes(value_bytes); + if let Ok(u_id) = usize::try_from(id) { + values.push( + parent_dict + .id_to_datavalue(u_id) + .expect("value used within tuple not found in dictionary"), + ); + } else { + panic!( + "failed to convert u32 to usize; platforms with less that 32bits are not supported" + ); + } + } + TUPLE_TYPE_NULLID64 => { + get_value_bytes!(8, pos_content, tuple_content, value_bytes); + let id = u64::from_be_bytes(value_bytes); + if let Ok(u_id) = usize::try_from(id) { + values.push(NullDataValue::new(u_id).into()); + } else { + panic!( + "failed to convert u64 to usize; dictionary surpasses the capabilities of this platform" + ); + } + } + TUPLE_TYPE_NULLID32 => { + get_value_bytes!(4, pos_content, tuple_content, value_bytes); + let id = u32::from_be_bytes(value_bytes); + if let Ok(u_id) = usize::try_from(id) { + values.push(NullDataValue::new(u_id).into()); + } else { + panic!( + "failed to convert u32 to usize; platforms with less that 32bits are not supported" + ); + } + } + TUPLE_TYPE_I64 => { + get_value_bytes!(8, pos_content, tuple_content, value_bytes); + values.push(AnyDataValue::new_integer_from_i64(i64::from_be_bytes( + value_bytes, + ))); + } + TUPLE_TYPE_U64 => { + get_value_bytes!(8, pos_content, tuple_content, value_bytes); + values.push(AnyDataValue::new_integer_from_u64(u64::from_be_bytes( + value_bytes, + ))); + } + TUPLE_TYPE_F32 => { + get_value_bytes!(4, pos_content, tuple_content, value_bytes); + values.push( + AnyDataValue::new_float_from_f32(f32::from_be_bytes(value_bytes)) + .expect("stored f32 value could not be recovered"), + ); + } + TUPLE_TYPE_F64 => { + get_value_bytes!(8, pos_content, tuple_content, value_bytes); + values.push( + AnyDataValue::new_double_from_f64(f64::from_be_bytes(value_bytes)) + .expect("stored f64 value could not be recovered"), + ); + } + TUPLE_TYPE_BOOL => { + get_value_bytes!(1, pos_content, tuple_content, value_bytes); + if value_bytes[0] == 0 { + values.push(AnyDataValue::new_boolean(false)); + } else { + values.push(AnyDataValue::new_boolean(true)); + } + } + _ => panic!("unexpected type byte {cur_type} for tuple value"), + } + } + + let chunks = values.into_iter().array_chunks().collect::>(); + Some(MapDataValue::from_chunks(label, chunks.iter()).into()) +} diff --git a/nemo-physical/src/dictionary/meta_dv_dict.rs b/nemo-physical/src/dictionary/meta_dv_dict.rs index 3351b3c72..16d5e4593 100644 --- a/nemo-physical/src/dictionary/meta_dv_dict.rs +++ b/nemo-physical/src/dictionary/meta_dv_dict.rs @@ -5,13 +5,12 @@ use crate::datavalues::{AnyDataValue, DataValue}; use crate::dictionary::NONEXISTING_ID_MARK; use crate::management::bytesized::{ByteSized, size_inner_vec_flat}; -use super::DvDict; -use super::StringDvDictionary; +use super::map_dv_dict::MapDvDict; use super::ranked_pair_iri_dv_dict::IriSplittingDvDictionary; use super::ranked_pair_other_dv_dict::OtherSplittingDvDictionary; use super::string_langstring_dv_dict::LangStringDvDictionary; use super::tuple_dv_dict::TupleDvDict; -use super::{AddResult, NullDvDictionary}; +use super::{AddResult, DvDict, NullDvDictionary, StringDvDictionary}; // /// Number of recent occurrences of a string pattern required for creating a bespoke dictionary // const DICT_THRESHOLD: u32 = 500; @@ -37,6 +36,8 @@ enum DictionaryType { Null, /// Dictionary for (variable-free) tuples Tuple, + /// Dictionary for (variable-free) maps + Map, // /// Dictionary for long strings (blobs) // Blob, // /// Dictionary for strings with a fixed prefix and suffix @@ -124,6 +125,7 @@ impl DictIterator { ValueDomain::Other => return md.other_dict, ValueDomain::Null => return md.null_dict, ValueDomain::Tuple => return md.tuple_dict, + ValueDomain::Map => return md.map_dict, ValueDomain::Boolean => return md.other_dict, // TODO: maybe not the best place, using a whole page for two values if there is not much "other" ValueDomain::UnsignedLong => return md.other_dict, // TODO: maybe not the best place either _ => {} @@ -173,6 +175,8 @@ pub struct MetaDvDictionary { null_dict: DictId, /// Id of the tuple dictionary, if any (otherwise [NO_DICT]) tuple_dict: DictId, + /// Id of the map dictionary, if any (otherwise [NO_DICT]) + map_dict: DictId, /// Ids of further general-purpose dictionaries, /// which might be used for any kind of datavalue. generic_dicts: Vec, @@ -199,6 +203,7 @@ impl Default for MetaDvDictionary { other_dict: NO_DICT, null_dict: NO_DICT, tuple_dict: NO_DICT, + map_dict: NO_DICT, // dict_candidates: LruCache::new(NonZeroUsize::new(150).unwrap()), //infix_dicts: HashMap::new(), generic_dicts: Vec::new(), @@ -211,6 +216,7 @@ impl Default for MetaDvDictionary { result.add_dictionary(DictionaryType::Other); result.add_dictionary(DictionaryType::Null); result.add_dictionary(DictionaryType::Tuple); + result.add_dictionary(DictionaryType::Map); result } @@ -359,6 +365,13 @@ impl MetaDvDictionary { } dict = Box::new(TupleDvDict::new()); self.tuple_dict = self.dicts.len(); + } + DictionaryType::Map => { + if self.map_dict != NO_DICT { + return; + } + dict = Box::new(MapDvDict::new()); + self.map_dict = self.dicts.len(); } // DictionaryType::Infix { // ref prefix, // ref suffix, @@ -595,12 +608,15 @@ impl ByteSized for MetaDvDictionary { mod test { use crate::{ datavalues::{ - AnyDataValue, IriDataValue, NullDataValue, TupleDataValue, syntax::XSD_PREFIX, + AnyDataValue, IriDataValue, MapDataValue, NullDataValue, TupleDataValue, + syntax::XSD_PREFIX, }, dictionary::{AddResult, DvDict}, }; use super::MetaDvDictionary; + #[cfg(not(miri))] + use test_log::test; #[test] fn add_and_get() { @@ -821,4 +837,53 @@ mod test { ); assert_eq!(dict.id_to_datavalue(dv_tuple_id), Some(dv_tuple2.clone())); } + + #[test] + fn add_and_get_map() { + let mut dict = MetaDvDictionary::new(); + + let dv_label = IriDataValue::new("f".to_string()); + let null_id = dict.fresh_null_id(); + let dv_null = dict.id_to_datavalue(null_id).unwrap(); + let dvs = vec![ + ( + AnyDataValue::new_plain_string("http://example.org".to_string()), + AnyDataValue::new_integer_from_i64(42), + ), + ( + AnyDataValue::new_integer_from_u64(u64::MAX - 3), + AnyDataValue::new_plain_string("another string".to_string()), + ), + (AnyDataValue::new_boolean(true), dv_null), + ( + AnyDataValue::new_float_from_f32(3.9914).expect("must work"), + AnyDataValue::new_double_from_f64(1.2345).expect("must work"), + ), + ( + AnyDataValue::new_iri("http://example.org".to_string()), + AnyDataValue::new_language_tagged_string("Hallo".to_string(), "de".to_string()), + ), + ( + AnyDataValue::new_other( + "abc".to_string(), + "http://example.org/mydatatype".to_string(), + ), + dv_label.into(), + ), + ]; + + let dv_map: AnyDataValue = MapDataValue::from_iter(dvs).into(); + + let ar = dict.add_datavalue(dv_map.clone()); + let AddResult::Fresh(dv_map_id) = ar else { + panic!("add failed: {ar:?}"); + }; + + assert_eq!(dict.datavalue_to_id(&dv_map), Some(dv_map_id)); + assert_eq!( + dict.add_datavalue(dv_map.clone()), + AddResult::Known(dv_map_id) + ); + assert_eq!(dict.id_to_datavalue(dv_map_id), Some(dv_map.clone())); + } } diff --git a/nemo-physical/src/dictionary/tuple_dv_dict.rs b/nemo-physical/src/dictionary/tuple_dv_dict.rs index 922dbe50b..75511076d 100644 --- a/nemo-physical/src/dictionary/tuple_dv_dict.rs +++ b/nemo-physical/src/dictionary/tuple_dv_dict.rs @@ -11,29 +11,29 @@ use super::meta_dv_dict::MetaDvDictionary; use super::{AddResult, ranked_pair_dictionary::GenericRankedPairDictionary}; /// Type constant that signifies no type in a tuple -const TUPLE_TYPE_NOTYPE: u8 = 0; +pub(super) const TUPLE_TYPE_NOTYPE: u8 = 0; /// Type constant to signify a 64bit dictionary id in a tuple -const TUPLE_TYPE_DICTID64: u8 = 1; +pub(super) const TUPLE_TYPE_DICTID64: u8 = 1; /// Type constant to signify a 32bit dictionary id in a tuple -const TUPLE_TYPE_DICTID32: u8 = 2; +pub(super) const TUPLE_TYPE_DICTID32: u8 = 2; /// Type constant to signify a 64bit null id in a tuple -const TUPLE_TYPE_NULLID64: u8 = 3; +pub(super) const TUPLE_TYPE_NULLID64: u8 = 3; /// Type constant to signify a 32bit null id in a tuple -const TUPLE_TYPE_NULLID32: u8 = 4; +pub(super) const TUPLE_TYPE_NULLID32: u8 = 4; /// Type constant to signify an i64 value in a tuple -const TUPLE_TYPE_I64: u8 = 5; +pub(super) const TUPLE_TYPE_I64: u8 = 5; /// Type constant to signify an u64 value in a tuple -const TUPLE_TYPE_U64: u8 = 6; +pub(super) const TUPLE_TYPE_U64: u8 = 6; /// Type constant to signify an f32 value in a tuple -const TUPLE_TYPE_F32: u8 = 7; +pub(super) const TUPLE_TYPE_F32: u8 = 7; /// Type constant to signify an f64 value in a tuple -const TUPLE_TYPE_F64: u8 = 8; +pub(super) const TUPLE_TYPE_F64: u8 = 8; /// Type constant to signify a boolean value in a tuple -const TUPLE_TYPE_BOOL: u8 = 9; +pub(super) const TUPLE_TYPE_BOOL: u8 = 9; /// Type constant to signify a 32bit dictionary id of a label (at the start of tuples) -const TUPLE_TYPE_LABELID32: u8 = 10; +pub(super) const TUPLE_TYPE_LABELID32: u8 = 10; /// Type constant to signify a 64bit dictionary id of a label (at the start of tuples) -const TUPLE_TYPE_LABELID64: u8 = 11; +pub(super) const TUPLE_TYPE_LABELID64: u8 = 11; crate::dictionary::bytes_buffer::declare_bytes_buffer!( TupleBytesPairDictBytesBuffer, @@ -202,7 +202,10 @@ fn push_usize_id_to_bytes( /// TODO: This function is very similar to [tuple_bytes]. Maybe consider (efficient) options /// for code sharing. However, putting large parts of code into macros is not very helpful either, /// since it leads to less readable code. -fn tuple_bytes_mut(parent_dict: &mut MetaDvDictionary, dv: AnyDataValue) -> (Vec, Vec) { +pub(super) fn tuple_bytes_mut( + parent_dict: &mut MetaDvDictionary, + dv: AnyDataValue, +) -> (Vec, Vec) { let mut tuple_type: Vec = Vec::with_capacity(8); // space for a short label and 4 distinct types let mut tuple_content: Vec = Vec::with_capacity(16); // space for 4 short values @@ -222,7 +225,7 @@ fn tuple_bytes_mut(parent_dict: &mut MetaDvDictionary, dv: AnyDataValue) -> (Vec let mut cur_type: u8; let mut prev_type: u8 = TUPLE_TYPE_NOTYPE; let mut last_type_change: usize = 0; - for i in 0..dv.len_unchecked() { + for i in 0..dv.tuple_len_unchecked() { let dvi = dv.tuple_element_unchecked(i); match dvi.value_domain() { ValueDomain::PlainString @@ -298,7 +301,10 @@ fn tuple_bytes_mut(parent_dict: &mut MetaDvDictionary, dv: AnyDataValue) -> (Vec /// TODO: This function is very similar to [tuple_bytes_mut]. Maybe consider (efficient) options /// for code sharing. However, putting large parts of code into macros is not very helpful either, /// since it leads to less readable code. -fn tuple_bytes(parent_dict: &MetaDvDictionary, dv: &AnyDataValue) -> Option<(Vec, Vec)> { +pub(super) fn tuple_bytes( + parent_dict: &MetaDvDictionary, + dv: &AnyDataValue, +) -> Option<(Vec, Vec)> { let mut tuple_type: Vec = Vec::with_capacity(8); // space for a short label and 4 distinct types let mut tuple_content: Vec = Vec::with_capacity(16); // space for 4 short values @@ -321,7 +327,7 @@ fn tuple_bytes(parent_dict: &MetaDvDictionary, dv: &AnyDataValue) -> Option<(Vec let mut cur_type: u8; let mut prev_type: u8 = TUPLE_TYPE_NOTYPE; let mut last_type_change: usize = 0; - for i in 0..dv.len_unchecked() { + for i in 0..dv.tuple_len_unchecked() { let dvi = dv.tuple_element_unchecked(i); match dvi.value_domain() { ValueDomain::PlainString diff --git a/nemo-physical/src/lib.rs b/nemo-physical/src/lib.rs index c8e714d14..2b0cc2a65 100644 --- a/nemo-physical/src/lib.rs +++ b/nemo-physical/src/lib.rs @@ -22,6 +22,7 @@ #![feature(iter_intersperse)] #![feature(test)] #![feature(slice_swap_unchecked)] +#![feature(iter_array_chunks)] mod benches; From 56a5843a07700e3e18bd24fc8e6a28480d22bffa Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Thu, 12 Jun 2025 19:25:05 +0200 Subject: [PATCH 2/9] WIP: Support JSON serialisation for AnyDataValue --- Cargo.lock | 7 ++ nemo-physical/src/datavalues/any_datavalue.rs | 17 ++- nemo/Cargo.toml | 1 + nemo/src/io/formats/json.rs | 2 + nemo/src/io/formats/json/datavalues.rs | 2 + nemo/src/io/formats/json/datavalues/other.rs | 19 +++ .../formats/json/datavalues/plain_string.rs | 19 +++ nemo/src/io/formats/json/reader.rs | 111 +----------------- nemo/src/io/formats/json/variants.rs | 81 +++++++++++++ nemo/src/io/formats/json/variants/default.rs | 64 ++++++++++ 10 files changed, 216 insertions(+), 107 deletions(-) create mode 100644 nemo/src/io/formats/json/datavalues.rs create mode 100644 nemo/src/io/formats/json/datavalues/other.rs create mode 100644 nemo/src/io/formats/json/datavalues/plain_string.rs create mode 100644 nemo/src/io/formats/json/variants.rs create mode 100644 nemo/src/io/formats/json/variants/default.rs diff --git a/Cargo.lock b/Cargo.lock index d27e2db51..8c25c620b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1636,6 +1636,7 @@ dependencies = [ "oxiri", "oxrdf", "oxrdfio", + "paste", "petgraph", "petgraph-graphml", "quickcheck", @@ -2147,6 +2148,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "path-slash" version = "0.2.1" diff --git a/nemo-physical/src/datavalues/any_datavalue.rs b/nemo-physical/src/datavalues/any_datavalue.rs index 31c7adc5b..c8f8867d0 100644 --- a/nemo-physical/src/datavalues/any_datavalue.rs +++ b/nemo-physical/src/datavalues/any_datavalue.rs @@ -45,7 +45,7 @@ impl DecimalType { /// Enum that can represent arbitrary [DataValue]s. #[derive(Debug, Clone)] -enum AnyDataValueEnum { +pub enum AnyDataValueEnum { /// Variant for representing [DataValue]s in [ValueDomain::PlainString]. PlainString(StringDataValue), /// Variant for representing [DataValue]s in [ValueDomain::LanguageTaggedString]. @@ -575,6 +575,21 @@ impl AnyDataValue { panic!("not a null value"); } } + + /// Construct a new [AnyDataValue] from an existing + /// [AnyDataValueEnum]. + /// + /// This is most useful for implementing deserialization; if the + /// underlying type of the value is known, the type-specific + /// constructors should be preferred. + pub fn from_enum(value: AnyDataValueEnum) -> Self { + Self(value) + } + + /// Returns the internal representation as an [AnyDataValueEnum]. + pub fn into_inner(self) -> AnyDataValueEnum { + self.0 + } } impl DataValue for AnyDataValue { diff --git a/nemo/Cargo.toml b/nemo/Cargo.toml index e22d7c8ee..5fa7a5fd0 100644 --- a/nemo/Cargo.toml +++ b/nemo/Cargo.toml @@ -58,6 +58,7 @@ delegate = "0.13.3" orx-imp-vec = "2.14" itertools = "0.14.0" indexmap = "2.10.0" +paste = "1.0.15" [dev-dependencies] env_logger = "*" diff --git a/nemo/src/io/formats/json.rs b/nemo/src/io/formats/json.rs index 9ba653a40..57f2d68c0 100644 --- a/nemo/src/io/formats/json.rs +++ b/nemo/src/io/formats/json.rs @@ -1,6 +1,8 @@ //! Handler for resources of type JSON (java script object notation). +pub(crate) mod datavalues; pub(crate) mod reader; +pub(crate) mod variants; use std::{io::Read, sync::Arc}; diff --git a/nemo/src/io/formats/json/datavalues.rs b/nemo/src/io/formats/json/datavalues.rs new file mode 100644 index 000000000..fb62b395a --- /dev/null +++ b/nemo/src/io/formats/json/datavalues.rs @@ -0,0 +1,2 @@ +pub(crate) mod other; +pub(crate) mod plain_string; diff --git a/nemo/src/io/formats/json/datavalues/other.rs b/nemo/src/io/formats/json/datavalues/other.rs new file mode 100644 index 000000000..8627e276a --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/other.rs @@ -0,0 +1,19 @@ +pub(crate) mod default { + use nemo_physical::datavalues::DataValue; + use serde::{Deserializer, Serializer}; + + pub(crate) fn deserialize<'de, T, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + todo!() + } + + pub(crate) fn serialize(value: &T, serializer: S) -> Result + where + T: DataValue, + S: Serializer, + { + serializer.serialize_str(&value.canonical_string()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/plain_string.rs b/nemo/src/io/formats/json/datavalues/plain_string.rs new file mode 100644 index 000000000..0a885ace7 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/plain_string.rs @@ -0,0 +1,19 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, StringDataValue}; + use serde::{Deserializer, Serializer}; + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + todo!() + } + + pub(crate) fn serialize(value: &T, serializer: S) -> Result + where + T: DataValue, + S: Serializer, + { + serializer.serialize_str(&value.canonical_string()) + } +} diff --git a/nemo/src/io/formats/json/reader.rs b/nemo/src/io/formats/json/reader.rs index bcb3a974a..802b74714 100644 --- a/nemo/src/io/formats/json/reader.rs +++ b/nemo/src/io/formats/json/reader.rs @@ -11,7 +11,8 @@ use nemo_physical::{ management::bytesized::ByteSized, tabular::filters::FilterTransformPattern, }; -use serde_json::Value; + +use super::variants; pub(crate) struct JsonReader { pub(super) read: T, @@ -53,111 +54,9 @@ impl TableProvider for JsonReader { self: Box, tuple_writer: &mut TupleWriter, ) -> Result<(), ReadingError> { - tuple_writer.set_patterns(self.patterns); - let value: Value = serde_json::from_reader(self.read).map_err(JsonReadingError)?; - let mut max_object_id = 0u64; - - let mut stack = vec![(max_object_id, value)]; - - let type_iri = AnyDataValue::new_iri("type".into()); - let value_iri = AnyDataValue::new_iri("value".into()); - - while let Some((object_id, current)) = stack.pop() { - let id = AnyDataValue::new_integer_from_u64(object_id); - - match current { - Value::Null => { - tuple_writer.add_tuple_value(id); - tuple_writer.add_tuple_value(type_iri.clone()); - - let type_field = AnyDataValue::new_plain_string("null".into()); - tuple_writer.add_tuple_value(type_field); - } - Value::Bool(value) => { - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(type_iri.clone()); - - let type_field = AnyDataValue::new_plain_string("bool".into()); - tuple_writer.add_tuple_value(type_field); - - tuple_writer.add_tuple_value(id); - tuple_writer.add_tuple_value(value_iri.clone()); - - let value_field = AnyDataValue::new_boolean(value); - tuple_writer.add_tuple_value(value_field); - } - Value::Number(value) => { - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(type_iri.clone()); - - let type_field = AnyDataValue::new_plain_string("number".into()); - tuple_writer.add_tuple_value(type_field); - - tuple_writer.add_tuple_value(id); - tuple_writer.add_tuple_value(value_iri.clone()); - - let value_field = if let Some(value) = value.as_i64() { - AnyDataValue::new_integer_from_i64(value) - } else if let Some(value) = value.as_u64() { - AnyDataValue::new_integer_from_u64(value) - } else { - AnyDataValue::new_double_from_f64( - value - .as_f64() - .expect("numeric value should always fit in i64, u64 or f64"), - )? - }; - tuple_writer.add_tuple_value(value_field); - } - Value::String(value) => { - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(type_iri.clone()); - - let type_field = AnyDataValue::new_plain_string("string".into()); - tuple_writer.add_tuple_value(type_field); - - tuple_writer.add_tuple_value(id); - tuple_writer.add_tuple_value(value_iri.clone()); - - let value_field = AnyDataValue::new_plain_string(value); - tuple_writer.add_tuple_value(value_field); - } - Value::Array(value) => { - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(type_iri.clone()); - - let type_field = AnyDataValue::new_plain_string("array".into()); - tuple_writer.add_tuple_value(type_field); - - for (i, element) in value.into_iter().enumerate() { - max_object_id += 1; - stack.push((max_object_id, element)); - - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(AnyDataValue::new_integer_from_u64(i as u64)); - tuple_writer - .add_tuple_value(AnyDataValue::new_integer_from_u64(max_object_id)) - } - } - Value::Object(value) => { - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(type_iri.clone()); - - let type_field = AnyDataValue::new_plain_string("object".into()); - tuple_writer.add_tuple_value(type_field); - - for (key, element) in value { - max_object_id += 1; - stack.push((max_object_id, element)); - - tuple_writer.add_tuple_value(id.clone()); - tuple_writer.add_tuple_value(AnyDataValue::new_plain_string(key)); - tuple_writer - .add_tuple_value(AnyDataValue::new_integer_from_u64(max_object_id)) - } - } - } - } + let value: variants::default::JsonAnyDataValue = + serde_json::from_reader(self.read).map_err(JsonReadingError)?; + tuple_writer.add_tuple_value(AnyDataValue::from(value)); Ok(()) } diff --git a/nemo/src/io/formats/json/variants.rs b/nemo/src/io/formats/json/variants.rs new file mode 100644 index 000000000..de0be3f7f --- /dev/null +++ b/nemo/src/io/formats/json/variants.rs @@ -0,0 +1,81 @@ +/// A helper macro to set up the boilerplate for a JSON variant. +/// +/// `$name` is the variant name, the macro will define a wrapper type +/// `JsonAnyDataValue` that is used for serializing and deserializing +/// an [AnyDataValue](nemo_physical::datavalues::AnyDataValue). +/// +/// For each variant in +/// [AnyDataValue](nemo_physical::datavalues::AnyDataValue), it takes +/// a specification of the form `$variant($type) => "$policy", where +/// `$variant` is the name of the variant, `$type` is the underlying +/// data value type, and `$policy` is a string of the form +/// `"datavalues::type::variant"`, which points to a module in the +/// [datavalues](crate::io::formats::json::datavalues) module +/// indicating which type and which variant to use for +/// serialization/deserialization. +macro_rules! json_variant { + ($name:ident; $($variant:ident($type:ty) => $policy:expr),+) => { + pub(crate) mod $name { + use serde::{Deserialize, Serialize}; + + use nemo_physical::datavalues::{any_datavalue::AnyDataValueEnum, *}; + use crate::io::formats::json::datavalues; + + #[derive(Deserialize, Serialize)] + pub(crate) enum JsonAnyDataValue { + $( + #[serde(with = $policy)] + $variant($type) + ),+ + } + + // this is mostly to ensure that we exhaustively match all variants + impl From for JsonAnyDataValue { + fn from(value: AnyDataValueEnum) -> Self { + use AnyDataValueEnum::*; + + match value { + $( $variant(value) => Self::$variant(value) ),+ + } + } + } + + impl From for JsonAnyDataValue { + fn from(value: AnyDataValue) -> Self { + Self::from(value.into_inner()) + } + } + + impl From for AnyDataValueEnum { + fn from(value: JsonAnyDataValue) -> Self { + use JsonAnyDataValue::*; + + match value { + $( $variant(value) => Self::$variant(value) ),+ + } + } + } + + impl From for AnyDataValue { + fn from(value: JsonAnyDataValue) -> Self { + Self::from_enum(AnyDataValueEnum::from(value)) + } + } + } + }; +} + +json_variant!(default; + PlainString(StringDataValue) => "datavalues::plain_string::default", + LanguageTaggedString(LangStringDataValue) => "datavalues::other::default", + Iri(IriDataValue) => "datavalues::other::default", + Float(FloatDataValue) => "datavalues::other::default", + Double(DoubleDataValue) => "datavalues::other::default", + UnsignedLong(UnsignedLongDataValue) => "datavalues::other::default", + Long(LongDataValue) => "datavalues::other::default", + Boolean(BooleanDataValue) => "datavalues::other::default", + Null(NullDataValue) => "datavalues::other::default", + Tuple(TupleDataValue) => "datavalues::other::default", + Map(MapDataValue) => "datavalues::other::default", + Other(OtherDataValue) => "datavalues::other::default" +); diff --git a/nemo/src/io/formats/json/variants/default.rs b/nemo/src/io/formats/json/variants/default.rs new file mode 100644 index 000000000..ca61f1cdb --- /dev/null +++ b/nemo/src/io/formats/json/variants/default.rs @@ -0,0 +1,64 @@ +use serde::{Deserialize, Serialize}; + +use nemo_physical::datavalues::{ + any_datavalue::AnyDataValueEnum, AnyDataValue, BooleanDataValue, DoubleDataValue, + FloatDataValue, IriDataValue, LangStringDataValue, LongDataValue, MapDataValue, NullDataValue, + OtherDataValue, StringDataValue, TupleDataValue, UnsignedLongDataValue, +}; + +use crate::io::formats::json::datavalues; + +#[derive(Deserialize, Serialize)] +#[serde(remote = "AnyDataValueEnum")] +pub(crate) enum AnyDataValueRef { + #[serde(with = "datavalues::plain_string::default")] + PlainString(StringDataValue), + #[serde(with = "datavalues::other::default")] + LanguageTaggedString(LangStringDataValue), + #[serde(with = "datavalues::other::default")] + Iri(IriDataValue), + #[serde(with = "datavalues::other::default")] + Float(FloatDataValue), + #[serde(with = "datavalues::other::default")] + Double(DoubleDataValue), + #[serde(with = "datavalues::other::default")] + UnsignedLong(UnsignedLongDataValue), + #[serde(with = "datavalues::other::default")] + Long(LongDataValue), + #[serde(with = "datavalues::other::default")] + Boolean(BooleanDataValue), + #[serde(with = "datavalues::other::default")] + Null(NullDataValue), + #[serde(with = "datavalues::other::default")] + Tuple(TupleDataValue), + #[serde(with = "datavalues::other::default")] + Map(MapDataValue), + #[serde(with = "datavalues::other::default")] + Other(OtherDataValue), +} + +impl From for AnyDataValueEnum { + fn from(value: AnyDataValueRef) -> Self { + use AnyDataValueRef::*; + match value { + PlainString(value) => Self::PlainString(value), + LanguageTaggedString(value) => Self::LanguageTaggedString(value), + Iri(value) => Self::Iri(value), + Float(value) => Self::Float(value), + Double(value) => Self::Double(value), + UnsignedLong(value) => Self::UnsignedLong(value), + Long(value) => Self::Long(value), + Boolean(value) => Self::Boolean(value), + Null(value) => Self::Null(value), + Tuple(value) => Self::Tuple(value), + Map(value) => Self::Map(value), + Other(value) => Self::Other(value), + } + } +} + +impl From for AnyDataValue { + fn from(value: AnyDataValueRef) -> Self { + Self::from_enum(AnyDataValueEnum::from(value)) + } +} From fa549747cac462d78f4d49adc2c8d52f7528c552 Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Wed, 16 Jul 2025 17:53:23 +0200 Subject: [PATCH 3/9] WIP: Support JSON serialization for AnyDataValue --- .../src/datavalues/float_datavalues.rs | 8 +-- nemo/src/io/formats/json/datavalues.rs | 3 + nemo/src/io/formats/json/datavalues/any.rs | 20 ++++++ nemo/src/io/formats/json/datavalues/double.rs | 39 +++++++++++ nemo/src/io/formats/json/datavalues/float.rs | 39 +++++++++++ .../formats/json/datavalues/plain_string.rs | 12 +++- nemo/src/io/formats/json/variants.rs | 17 +++-- nemo/src/io/formats/json/variants/default.rs | 64 ------------------- 8 files changed, 126 insertions(+), 76 deletions(-) create mode 100644 nemo/src/io/formats/json/datavalues/any.rs create mode 100644 nemo/src/io/formats/json/datavalues/double.rs create mode 100644 nemo/src/io/formats/json/datavalues/float.rs delete mode 100644 nemo/src/io/formats/json/variants/default.rs diff --git a/nemo-physical/src/datavalues/float_datavalues.rs b/nemo-physical/src/datavalues/float_datavalues.rs index b0df21b54..585094ec7 100644 --- a/nemo-physical/src/datavalues/float_datavalues.rs +++ b/nemo-physical/src/datavalues/float_datavalues.rs @@ -17,8 +17,8 @@ impl FloatDataValue { /// Use the given f32 as a [FloatDataValue]. /// /// # Errors - /// The given `value` is NaN. - pub(crate) fn from_f32(value: f32) -> Result { + /// The given `value` is NaN or infinite. + pub fn from_f32(value: f32) -> Result { if !value.is_finite() { return Err(DataValueCreationError::NonFiniteFloat {}); } @@ -28,7 +28,7 @@ impl FloatDataValue { /// Use the given f32 as a [FloatDataValue]. /// /// # Panics - /// The given `value` is NaN. + /// The given `value` is NaN or infinite. pub(crate) fn from_f32_unchecked(value: f32) -> Self { if !value.is_finite() { panic!( @@ -101,7 +101,7 @@ impl DoubleDataValue { /// /// # Errors /// The given `value` is NaN or an infinity. - pub(crate) fn from_f64(value: f64) -> Result { + pub fn from_f64(value: f64) -> Result { if !value.is_finite() { return Err(DataValueCreationError::NonFiniteFloat {}); } diff --git a/nemo/src/io/formats/json/datavalues.rs b/nemo/src/io/formats/json/datavalues.rs index fb62b395a..e6ce57dd1 100644 --- a/nemo/src/io/formats/json/datavalues.rs +++ b/nemo/src/io/formats/json/datavalues.rs @@ -1,2 +1,5 @@ +pub(crate) mod any; +pub(crate) mod double; +pub(crate) mod float; pub(crate) mod other; pub(crate) mod plain_string; diff --git a/nemo/src/io/formats/json/datavalues/any.rs b/nemo/src/io/formats/json/datavalues/any.rs new file mode 100644 index 000000000..bcc0424e5 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/any.rs @@ -0,0 +1,20 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{AnyDataValue, DataValue}; + use serde::{Deserializer, Serializer}; + + pub(crate) fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: DataValue, + D: Deserializer<'de>, + { + todo!() + } + + pub(crate) fn serialize(value: &T, serializer: S) -> Result + where + T: DataValue, + S: Serializer, + { + serializer.serialize_str(&value.canonical_string()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/double.rs b/nemo/src/io/formats/json/datavalues/double.rs new file mode 100644 index 000000000..ae121604f --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/double.rs @@ -0,0 +1,39 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, DoubleDataValue}; + use serde::{de::Visitor, ser::Error, Deserializer, Serializer}; + + struct DoubleVisitor {} + + impl<'de> Visitor<'de> for DoubleVisitor { + type Value = DoubleDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a finite double (f64) number") + } + + fn visit_f64(self, v: f64) -> Result + where + E: serde::de::Error, + { + DoubleDataValue::from_f64(v).map_err(|err| E::custom(err.to_string())) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_f64(DoubleVisitor {}) + } + + pub(crate) fn serialize(value: &DoubleDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_f64( + value + .to_f64() + .ok_or_else(|| S::Error::custom("value is not a Double"))?, + ) + } +} diff --git a/nemo/src/io/formats/json/datavalues/float.rs b/nemo/src/io/formats/json/datavalues/float.rs new file mode 100644 index 000000000..5b98a2840 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/float.rs @@ -0,0 +1,39 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, FloatDataValue}; + use serde::{de::Visitor, ser::Error, Deserializer, Serializer}; + + struct FloatVisitor {} + + impl<'de> Visitor<'de> for FloatVisitor { + type Value = FloatDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a finite float (f32) number") + } + + fn visit_f32(self, v: f32) -> Result + where + E: serde::de::Error, + { + FloatDataValue::from_f32(v).map_err(|err| E::custom(err.to_string())) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_f32(FloatVisitor {}) + } + + pub(crate) fn serialize(value: &FloatDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_f32( + value + .to_f32() + .ok_or_else(|| S::Error::custom("value is not a Float"))?, + ) + } +} diff --git a/nemo/src/io/formats/json/datavalues/plain_string.rs b/nemo/src/io/formats/json/datavalues/plain_string.rs index 0a885ace7..35361c52b 100644 --- a/nemo/src/io/formats/json/datavalues/plain_string.rs +++ b/nemo/src/io/formats/json/datavalues/plain_string.rs @@ -1,6 +1,16 @@ pub(crate) mod default { use nemo_physical::datavalues::{DataValue, StringDataValue}; - use serde::{Deserializer, Serializer}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct PlainStringVisitor {} + + impl<'de> Visitor<'de> for PlainStringVisitor { + type Value = StringDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a string") + } + } pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result where diff --git a/nemo/src/io/formats/json/variants.rs b/nemo/src/io/formats/json/variants.rs index de0be3f7f..1ee91069c 100644 --- a/nemo/src/io/formats/json/variants.rs +++ b/nemo/src/io/formats/json/variants.rs @@ -65,17 +65,20 @@ macro_rules! json_variant { }; } +// Order is important here: deserializing will take the first +// successful variant, so we need to try, e.g., values serialized into +// strings (such as IRIs or language strings) before plain strings. json_variant!(default; + Null(NullDataValue) => "datavalues::any::default", + Tuple(TupleDataValue) => "datavalues::other::default", + Map(MapDataValue) => "datavalues::other::default", + LanguageTaggedString(LangStringDataValue) => "datavalues::any::default", + Iri(IriDataValue) => "datavalues::any::default", PlainString(StringDataValue) => "datavalues::plain_string::default", - LanguageTaggedString(LangStringDataValue) => "datavalues::other::default", - Iri(IriDataValue) => "datavalues::other::default", - Float(FloatDataValue) => "datavalues::other::default", - Double(DoubleDataValue) => "datavalues::other::default", UnsignedLong(UnsignedLongDataValue) => "datavalues::other::default", Long(LongDataValue) => "datavalues::other::default", + Float(FloatDataValue) => "datavalues::float::default", + Double(DoubleDataValue) => "datavalues::double::default", Boolean(BooleanDataValue) => "datavalues::other::default", - Null(NullDataValue) => "datavalues::other::default", - Tuple(TupleDataValue) => "datavalues::other::default", - Map(MapDataValue) => "datavalues::other::default", Other(OtherDataValue) => "datavalues::other::default" ); diff --git a/nemo/src/io/formats/json/variants/default.rs b/nemo/src/io/formats/json/variants/default.rs deleted file mode 100644 index ca61f1cdb..000000000 --- a/nemo/src/io/formats/json/variants/default.rs +++ /dev/null @@ -1,64 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use nemo_physical::datavalues::{ - any_datavalue::AnyDataValueEnum, AnyDataValue, BooleanDataValue, DoubleDataValue, - FloatDataValue, IriDataValue, LangStringDataValue, LongDataValue, MapDataValue, NullDataValue, - OtherDataValue, StringDataValue, TupleDataValue, UnsignedLongDataValue, -}; - -use crate::io::formats::json::datavalues; - -#[derive(Deserialize, Serialize)] -#[serde(remote = "AnyDataValueEnum")] -pub(crate) enum AnyDataValueRef { - #[serde(with = "datavalues::plain_string::default")] - PlainString(StringDataValue), - #[serde(with = "datavalues::other::default")] - LanguageTaggedString(LangStringDataValue), - #[serde(with = "datavalues::other::default")] - Iri(IriDataValue), - #[serde(with = "datavalues::other::default")] - Float(FloatDataValue), - #[serde(with = "datavalues::other::default")] - Double(DoubleDataValue), - #[serde(with = "datavalues::other::default")] - UnsignedLong(UnsignedLongDataValue), - #[serde(with = "datavalues::other::default")] - Long(LongDataValue), - #[serde(with = "datavalues::other::default")] - Boolean(BooleanDataValue), - #[serde(with = "datavalues::other::default")] - Null(NullDataValue), - #[serde(with = "datavalues::other::default")] - Tuple(TupleDataValue), - #[serde(with = "datavalues::other::default")] - Map(MapDataValue), - #[serde(with = "datavalues::other::default")] - Other(OtherDataValue), -} - -impl From for AnyDataValueEnum { - fn from(value: AnyDataValueRef) -> Self { - use AnyDataValueRef::*; - match value { - PlainString(value) => Self::PlainString(value), - LanguageTaggedString(value) => Self::LanguageTaggedString(value), - Iri(value) => Self::Iri(value), - Float(value) => Self::Float(value), - Double(value) => Self::Double(value), - UnsignedLong(value) => Self::UnsignedLong(value), - Long(value) => Self::Long(value), - Boolean(value) => Self::Boolean(value), - Null(value) => Self::Null(value), - Tuple(value) => Self::Tuple(value), - Map(value) => Self::Map(value), - Other(value) => Self::Other(value), - } - } -} - -impl From for AnyDataValue { - fn from(value: AnyDataValueRef) -> Self { - Self::from_enum(AnyDataValueEnum::from(value)) - } -} From 3d1433c559346b8d7c9407d6c52bb4a04d64745d Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Thu, 17 Jul 2025 15:04:24 +0200 Subject: [PATCH 4/9] WIP: Support JSON serialization for AnyDataValue --- .../src/datavalues/string_datavalue.rs | 2 +- nemo/src/io/formats/json/datavalues.rs | 2 +- nemo/src/io/formats/json/datavalues/any.rs | 20 ---------- nemo/src/io/formats/json/datavalues/null.rs | 39 +++++++++++++++++++ .../formats/json/datavalues/plain_string.rs | 16 +++++++- nemo/src/parser/input.rs | 14 ++++++- 6 files changed, 69 insertions(+), 24 deletions(-) delete mode 100644 nemo/src/io/formats/json/datavalues/any.rs create mode 100644 nemo/src/io/formats/json/datavalues/null.rs diff --git a/nemo-physical/src/datavalues/string_datavalue.rs b/nemo-physical/src/datavalues/string_datavalue.rs index 3e13591b1..6ef1f8e94 100644 --- a/nemo-physical/src/datavalues/string_datavalue.rs +++ b/nemo-physical/src/datavalues/string_datavalue.rs @@ -13,7 +13,7 @@ pub struct StringDataValue(String); impl StringDataValue { /// Constructor. - pub(crate) fn new(string: String) -> Self { + pub fn new(string: String) -> Self { StringDataValue(string) } } diff --git a/nemo/src/io/formats/json/datavalues.rs b/nemo/src/io/formats/json/datavalues.rs index e6ce57dd1..5bb0fd7f2 100644 --- a/nemo/src/io/formats/json/datavalues.rs +++ b/nemo/src/io/formats/json/datavalues.rs @@ -1,5 +1,5 @@ -pub(crate) mod any; pub(crate) mod double; pub(crate) mod float; +pub(crate) mod null; pub(crate) mod other; pub(crate) mod plain_string; diff --git a/nemo/src/io/formats/json/datavalues/any.rs b/nemo/src/io/formats/json/datavalues/any.rs deleted file mode 100644 index bcc0424e5..000000000 --- a/nemo/src/io/formats/json/datavalues/any.rs +++ /dev/null @@ -1,20 +0,0 @@ -pub(crate) mod default { - use nemo_physical::datavalues::{AnyDataValue, DataValue}; - use serde::{Deserializer, Serializer}; - - pub(crate) fn deserialize<'de, T, D>(deserializer: D) -> Result - where - T: DataValue, - D: Deserializer<'de>, - { - todo!() - } - - pub(crate) fn serialize(value: &T, serializer: S) -> Result - where - T: DataValue, - S: Serializer, - { - serializer.serialize_str(&value.canonical_string()) - } -} diff --git a/nemo/src/io/formats/json/datavalues/null.rs b/nemo/src/io/formats/json/datavalues/null.rs new file mode 100644 index 000000000..300c7c311 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/null.rs @@ -0,0 +1,39 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, NullDataValue, StringDataValue}; + use nom::InputLength; + use serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }; + + use crate::parser::{ + ast::{expression::basic::blank::Blank, ProgramAST}, + input::ParserInput, + }; + + struct NullVisitor {} + + impl<'de> Visitor<'de> for NullVisitor { + type Value = NullDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a named null `_:`") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + let (rest, blank) = Blank::parse(ParserInput::stateless(v)) + .map_err(|err| Error::custom(err.to_string()))?; + + if rest.input_len() > 0 { + Err(Error::custom(format!( + "unexpected `{rest}` after named null" + ))) + } else { + Ok(NullDataValue::new(blank.name())) + } + } + } +} diff --git a/nemo/src/io/formats/json/datavalues/plain_string.rs b/nemo/src/io/formats/json/datavalues/plain_string.rs index 35361c52b..765d75761 100644 --- a/nemo/src/io/formats/json/datavalues/plain_string.rs +++ b/nemo/src/io/formats/json/datavalues/plain_string.rs @@ -10,13 +10,27 @@ pub(crate) mod default { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "a string") } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + self.visit_string(v.to_string()) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(StringDataValue::new(v)) + } } pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - todo!() + deserializer.deserialize_string(PlainStringVisitor {}) } pub(crate) fn serialize(value: &T, serializer: S) -> Result diff --git a/nemo/src/parser/input.rs b/nemo/src/parser/input.rs index fddca6eb1..360c8ff5d 100644 --- a/nemo/src/parser/input.rs +++ b/nemo/src/parser/input.rs @@ -1,6 +1,9 @@ //! This module defines [ParserInput]. -use std::str::{CharIndices, Chars}; +use std::{ + cell::RefCell, + str::{CharIndices, Chars}, +}; use nom::{ AsBytes, IResult, InputIter, InputLength, InputTake, InputTakeAtPosition, error::ErrorKind, @@ -23,6 +26,15 @@ impl<'a> ParserInput<'a> { state, } } + + pub(crate) fn stateless(input: &'a str) -> Self { + Self::new( + input, + ParserState { + errors: Rc::new(RefCell::new(Vec::new())), + }, + ) + } } impl AsBytes for ParserInput<'_> { From 38471943d2b73accce4451a1ab65013894a77cb1 Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Tue, 5 Aug 2025 22:44:30 +0200 Subject: [PATCH 5/9] WIP: Support serializing JSON as AnyDataValue --- Cargo.lock | 15 +++++++++++++-- nemo/Cargo.toml | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c25c620b..25cf596dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1646,6 +1646,7 @@ dependencies = [ "sanitise-file-name", "serde", "serde_json", + "serde_state", "similar-string", "spargebra", "strum", @@ -2784,6 +2785,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_state" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740753f904603d9f3b7130b13125be3a470aca9e6616da18cb96d9291bd6a72a" +dependencies = [ + "serde", + "serde_derive", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2903,9 +2914,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.107" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", diff --git a/nemo/Cargo.toml b/nemo/Cargo.toml index 5fa7a5fd0..f28c9ddb7 100644 --- a/nemo/Cargo.toml +++ b/nemo/Cargo.toml @@ -59,6 +59,7 @@ orx-imp-vec = "2.14" itertools = "0.14.0" indexmap = "2.10.0" paste = "1.0.15" +serde_state = { version = "0.4.8", features = ["derive"] } [dev-dependencies] env_logger = "*" From 63509744f0a97af05b9935c86c24fefca2034f49 Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Tue, 12 Aug 2025 00:59:02 +0200 Subject: [PATCH 6/9] WIP: Support serialising JSON as AnyDataValue --- .../src/datavalues/boolean_datavalue.rs | 2 +- .../src/datavalues/integer_datavalues.rs | 4 +- .../src/datavalues/lang_string_datavalue.rs | 2 +- nemo/src/io/formats/json/datavalues.rs | 7 ++ .../src/io/formats/json/datavalues/boolean.rs | 35 ++++++++++ nemo/src/io/formats/json/datavalues/iri.rs | 59 +++++++++++++++++ .../json/datavalues/language_string.rs | 66 +++++++++++++++++++ nemo/src/io/formats/json/datavalues/long.rs | 56 ++++++++++++++++ nemo/src/io/formats/json/datavalues/map.rs | 35 ++++++++++ nemo/src/io/formats/json/datavalues/null.rs | 20 +++++- nemo/src/io/formats/json/datavalues/other.rs | 2 +- nemo/src/io/formats/json/datavalues/tuple.rs | 35 ++++++++++ .../io/formats/json/datavalues/unsigned.rs | 59 +++++++++++++++++ nemo/src/io/formats/json/variants.rs | 16 ++--- nemo/src/parser/input.rs | 1 + 15 files changed, 383 insertions(+), 16 deletions(-) create mode 100644 nemo/src/io/formats/json/datavalues/boolean.rs create mode 100644 nemo/src/io/formats/json/datavalues/iri.rs create mode 100644 nemo/src/io/formats/json/datavalues/language_string.rs create mode 100644 nemo/src/io/formats/json/datavalues/long.rs create mode 100644 nemo/src/io/formats/json/datavalues/map.rs create mode 100644 nemo/src/io/formats/json/datavalues/tuple.rs create mode 100644 nemo/src/io/formats/json/datavalues/unsigned.rs diff --git a/nemo-physical/src/datavalues/boolean_datavalue.rs b/nemo-physical/src/datavalues/boolean_datavalue.rs index 759d5aac6..864bcaca7 100644 --- a/nemo-physical/src/datavalues/boolean_datavalue.rs +++ b/nemo-physical/src/datavalues/boolean_datavalue.rs @@ -15,7 +15,7 @@ pub struct BooleanDataValue(bool); impl BooleanDataValue { /// Create a new [BooleanDataValue]. - pub(crate) fn new(value: bool) -> Self { + pub fn new(value: bool) -> Self { Self(value) } } diff --git a/nemo-physical/src/datavalues/integer_datavalues.rs b/nemo-physical/src/datavalues/integer_datavalues.rs index bb4d67b19..458a2d311 100644 --- a/nemo-physical/src/datavalues/integer_datavalues.rs +++ b/nemo-physical/src/datavalues/integer_datavalues.rs @@ -30,7 +30,7 @@ pub struct UnsignedLongDataValue(u64); impl UnsignedLongDataValue { /// Constructor. - pub(crate) fn new(value: u64) -> Self { + pub fn new(value: u64) -> Self { UnsignedLongDataValue(value) } } @@ -114,7 +114,7 @@ pub struct LongDataValue(i64); impl LongDataValue { /// Constructor. - pub(crate) fn new(value: i64) -> Self { + pub fn new(value: i64) -> Self { LongDataValue(value) } } diff --git a/nemo-physical/src/datavalues/lang_string_datavalue.rs b/nemo-physical/src/datavalues/lang_string_datavalue.rs index 2fa58f2dd..542150149 100644 --- a/nemo-physical/src/datavalues/lang_string_datavalue.rs +++ b/nemo-physical/src/datavalues/lang_string_datavalue.rs @@ -21,7 +21,7 @@ impl LangStringDataValue { /// /// The langauge tag is normalized to lower case, following RDF Schema 1.1, which states that /// "The value space of language tags is always in lower case." - pub(crate) fn new(string: String, lang_tag: String) -> Self { + pub fn new(string: String, lang_tag: String) -> Self { LangStringDataValue(string, lang_tag.to_ascii_lowercase()) } } diff --git a/nemo/src/io/formats/json/datavalues.rs b/nemo/src/io/formats/json/datavalues.rs index 5bb0fd7f2..6a0a176ec 100644 --- a/nemo/src/io/formats/json/datavalues.rs +++ b/nemo/src/io/formats/json/datavalues.rs @@ -1,5 +1,12 @@ +pub(crate) mod boolean; pub(crate) mod double; pub(crate) mod float; +pub(crate) mod iri; +pub(crate) mod language_string; +pub(crate) mod long; +pub(crate) mod map; pub(crate) mod null; pub(crate) mod other; pub(crate) mod plain_string; +pub(crate) mod tuple; +pub(crate) mod unsigned; diff --git a/nemo/src/io/formats/json/datavalues/boolean.rs b/nemo/src/io/formats/json/datavalues/boolean.rs new file mode 100644 index 000000000..6a44af738 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/boolean.rs @@ -0,0 +1,35 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{BooleanDataValue, DataValue}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct BooleanVisitor {} + + impl<'de> Visitor<'de> for BooleanVisitor { + type Value = BooleanDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a boolean") + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Ok(BooleanDataValue::new(v)) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(BooleanVisitor {}) + } + + pub(crate) fn serialize(value: &BooleanDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(value.to_boolean_unchecked()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/iri.rs b/nemo/src/io/formats/json/datavalues/iri.rs new file mode 100644 index 000000000..95ba11cbd --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/iri.rs @@ -0,0 +1,59 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, IriDataValue}; + use nom::InputLength; + use serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }; + + use crate::parser::{ + ast::{expression::basic::iri::Iri, ProgramAST}, + input::ParserInput, + }; + + struct IriVisitor {} + + impl<'de> Visitor<'de> for IriVisitor { + type Value = IriDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "an IRI") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + self.visit_string(v.to_string()) + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + let (rest, value) = Iri::parse(ParserInput::stateless(&v)) + .map_err(|err| Error::custom(err.to_string()))?; + + if rest.input_len() > 0 { + Err(Error::custom(format!("unexpected `{rest}` after IRI"))) + } else { + Ok(IriDataValue::new(value.content())) + } + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(IriVisitor {}) + } + + pub(crate) fn serialize(value: &T, serializer: S) -> Result + where + T: DataValue, + S: Serializer, + { + serializer.serialize_str(&value.canonical_string()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/language_string.rs b/nemo/src/io/formats/json/datavalues/language_string.rs new file mode 100644 index 000000000..3361be11e --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/language_string.rs @@ -0,0 +1,66 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, LangStringDataValue}; + use nom::InputLength; + use serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }; + + use crate::parser::{ + ast::{expression::basic::string::StringLiteral, ProgramAST}, + input::ParserInput, + }; + + struct LangStringVisitor {} + + impl<'de> Visitor<'de> for LangStringVisitor { + type Value = LangStringDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a language-tagged string") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + self.visit_string(v.to_string()) + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + let (rest, value) = StringLiteral::parse(ParserInput::stateless(&v)) + .map_err(|err| Error::custom(err.to_string()))?; + + if rest.input_len() > 0 { + Err(Error::custom(format!( + "unexpected `{rest}` after language-tagged string" + ))) + } else { + Ok(LangStringDataValue::new( + value.content(), + value + .language_tag() + .ok_or(Error::custom("string literal does not have a language tag"))?, + )) + } + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(LangStringVisitor {}) + } + + pub(crate) fn serialize(value: &T, serializer: S) -> Result + where + T: DataValue, + S: Serializer, + { + serializer.serialize_str(&value.canonical_string()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/long.rs b/nemo/src/io/formats/json/datavalues/long.rs new file mode 100644 index 000000000..50dc26373 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/long.rs @@ -0,0 +1,56 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, LongDataValue}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct LongVisitor {} + + impl<'de> Visitor<'de> for LongVisitor { + type Value = LongDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a signed integer fitting into i64") + } + + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, + { + Ok(LongDataValue::new(v)) + } + + fn visit_i32(self, v: i32) -> Result + where + E: serde::de::Error, + { + Ok(LongDataValue::new(i64::from(v))) + } + + fn visit_i16(self, v: i16) -> Result + where + E: serde::de::Error, + { + Ok(LongDataValue::new(i64::from(v))) + } + + fn visit_i8(self, v: i8) -> Result + where + E: serde::de::Error, + { + Ok(LongDataValue::new(i64::from(v))) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_i64(LongVisitor {}) + } + + pub(crate) fn serialize(value: &LongDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_i64(value.to_i64_unchecked()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/map.rs b/nemo/src/io/formats/json/datavalues/map.rs new file mode 100644 index 000000000..09059d632 --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/map.rs @@ -0,0 +1,35 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, MapDataValue}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct MapVisitor {} + + impl<'de> Visitor<'de> for MapVisitor { + type Value = MapDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a boolean") + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Ok(MapDataValue::new(v)) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(MapVisitor {}) + } + + pub(crate) fn serialize(value: &MapDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(value.to_boolean_unchecked()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/null.rs b/nemo/src/io/formats/json/datavalues/null.rs index 300c7c311..0976fe30c 100644 --- a/nemo/src/io/formats/json/datavalues/null.rs +++ b/nemo/src/io/formats/json/datavalues/null.rs @@ -1,5 +1,5 @@ pub(crate) mod default { - use nemo_physical::datavalues::{DataValue, NullDataValue, StringDataValue}; + use nemo_physical::datavalues::{DataValue, NullDataValue}; use nom::InputLength; use serde::{ de::{Error, Visitor}, @@ -24,7 +24,7 @@ pub(crate) mod default { where E: Error, { - let (rest, blank) = Blank::parse(ParserInput::stateless(v)) + let (rest, _blank) = Blank::parse(ParserInput::stateless(v)) .map_err(|err| Error::custom(err.to_string()))?; if rest.input_len() > 0 { @@ -32,8 +32,22 @@ pub(crate) mod default { "unexpected `{rest}` after named null" ))) } else { - Ok(NullDataValue::new(blank.name())) + todo!("figure out how to turn this into a null") } } } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(NullVisitor {}) + } + + pub(crate) fn serialize(value: &NullDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&value.canonical_string()) + } } diff --git a/nemo/src/io/formats/json/datavalues/other.rs b/nemo/src/io/formats/json/datavalues/other.rs index 8627e276a..11b0a8434 100644 --- a/nemo/src/io/formats/json/datavalues/other.rs +++ b/nemo/src/io/formats/json/datavalues/other.rs @@ -2,7 +2,7 @@ pub(crate) mod default { use nemo_physical::datavalues::DataValue; use serde::{Deserializer, Serializer}; - pub(crate) fn deserialize<'de, T, D>(deserializer: D) -> Result + pub(crate) fn deserialize<'de, T, D>(_deserializer: D) -> Result where D: Deserializer<'de>, { diff --git a/nemo/src/io/formats/json/datavalues/tuple.rs b/nemo/src/io/formats/json/datavalues/tuple.rs new file mode 100644 index 000000000..1451eb5cc --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/tuple.rs @@ -0,0 +1,35 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, TupleDataValue}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct TupleVisitor {} + + impl<'de> Visitor<'de> for TupleVisitor { + type Value = TupleDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a boolean") + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Ok(TupleDataValue::new(v)) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(TupleVisitor {}) + } + + pub(crate) fn serialize(value: &TupleDataValue, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(value.to_boolean_unchecked()) + } +} diff --git a/nemo/src/io/formats/json/datavalues/unsigned.rs b/nemo/src/io/formats/json/datavalues/unsigned.rs new file mode 100644 index 000000000..d8ac105af --- /dev/null +++ b/nemo/src/io/formats/json/datavalues/unsigned.rs @@ -0,0 +1,59 @@ +pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, UnsignedLongDataValue}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct UnsignedVisitor {} + + impl<'de> Visitor<'de> for UnsignedVisitor { + type Value = UnsignedLongDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "an unsigned integer fitting into u64") + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + Ok(UnsignedLongDataValue::new(v)) + } + + fn visit_u32(self, v: u32) -> Result + where + E: serde::de::Error, + { + Ok(UnsignedLongDataValue::new(u64::from(v))) + } + + fn visit_u16(self, v: u16) -> Result + where + E: serde::de::Error, + { + Ok(UnsignedLongDataValue::new(u64::from(v))) + } + + fn visit_u8(self, v: u8) -> Result + where + E: serde::de::Error, + { + Ok(UnsignedLongDataValue::new(u64::from(v))) + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u64(UnsignedVisitor {}) + } + + pub(crate) fn serialize( + value: &UnsignedLongDataValue, + serializer: S, + ) -> Result + where + S: Serializer, + { + serializer.serialize_u64(value.to_u64_unchecked()) + } +} diff --git a/nemo/src/io/formats/json/variants.rs b/nemo/src/io/formats/json/variants.rs index 1ee91069c..cd3428ddb 100644 --- a/nemo/src/io/formats/json/variants.rs +++ b/nemo/src/io/formats/json/variants.rs @@ -69,16 +69,16 @@ macro_rules! json_variant { // successful variant, so we need to try, e.g., values serialized into // strings (such as IRIs or language strings) before plain strings. json_variant!(default; - Null(NullDataValue) => "datavalues::any::default", - Tuple(TupleDataValue) => "datavalues::other::default", - Map(MapDataValue) => "datavalues::other::default", - LanguageTaggedString(LangStringDataValue) => "datavalues::any::default", - Iri(IriDataValue) => "datavalues::any::default", + Null(NullDataValue) => "datavalues::null::default", + Tuple(TupleDataValue) => "datavalues::tuple::default", + Map(MapDataValue) => "datavalues::map::default", + LanguageTaggedString(LangStringDataValue) => "datavalues::language_string::default", + Iri(IriDataValue) => "datavalues::iri::default", PlainString(StringDataValue) => "datavalues::plain_string::default", - UnsignedLong(UnsignedLongDataValue) => "datavalues::other::default", - Long(LongDataValue) => "datavalues::other::default", + UnsignedLong(UnsignedLongDataValue) => "datavalues::unsigned::default", + Long(LongDataValue) => "datavalues::long::default", Float(FloatDataValue) => "datavalues::float::default", Double(DoubleDataValue) => "datavalues::double::default", - Boolean(BooleanDataValue) => "datavalues::other::default", + Boolean(BooleanDataValue) => "datavalues::boolean::default", Other(OtherDataValue) => "datavalues::other::default" ); diff --git a/nemo/src/parser/input.rs b/nemo/src/parser/input.rs index 360c8ff5d..67f314bef 100644 --- a/nemo/src/parser/input.rs +++ b/nemo/src/parser/input.rs @@ -2,6 +2,7 @@ use std::{ cell::RefCell, + rc::Rc, str::{CharIndices, Chars}, }; From 4e4695bdd038928e08cdd83530aa11daf3945afa Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Wed, 13 Aug 2025 19:37:21 +0200 Subject: [PATCH 7/9] WIP: Support serialising JSON as AnyDataValue --- nemo-physical/src/datavalues/map_datavalue.rs | 3 +- .../src/datavalues/other_datavalue.rs | 2 +- .../src/datavalues/tuple_datavalue.rs | 1 - nemo/src/io/formats/json/datavalues/map.rs | 41 ++++++-- nemo/src/io/formats/json/datavalues/other.rs | 58 ++++++++++- nemo/src/io/formats/json/datavalues/tuple.rs | 37 ++++++-- nemo/src/io/formats/json/variants.rs | 1 + .../testcases/data-formats/json/works.json | 95 ++++++++++++++++++- 8 files changed, 215 insertions(+), 23 deletions(-) diff --git a/nemo-physical/src/datavalues/map_datavalue.rs b/nemo-physical/src/datavalues/map_datavalue.rs index ad72799c6..6bd57a494 100644 --- a/nemo-physical/src/datavalues/map_datavalue.rs +++ b/nemo-physical/src/datavalues/map_datavalue.rs @@ -22,7 +22,6 @@ pub struct MapDataValue { impl MapDataValue { /// Constructor. - #[allow(dead_code)] pub fn new>( label: Option, pairs_iter: T, @@ -121,7 +120,7 @@ impl DataValue for MapDataValue { let pair = index / 2; let (key, value) = self.pairs.iter().nth(pair).expect("unchecked method"); - match index % 2 == 0 { + match index.is_multiple_of(2) { true => key, false => value, } diff --git a/nemo-physical/src/datavalues/other_datavalue.rs b/nemo-physical/src/datavalues/other_datavalue.rs index e8c518af1..2812c8524 100644 --- a/nemo-physical/src/datavalues/other_datavalue.rs +++ b/nemo-physical/src/datavalues/other_datavalue.rs @@ -11,7 +11,7 @@ pub struct OtherDataValue(String, String); impl OtherDataValue { /// Constructor. We do not currently check if the datatype IRI refers to a /// known type that is not really in [ValueDomain::Other]. - pub(crate) fn new(lexical_value: String, datatype_iri: String) -> Self { + pub fn new(lexical_value: String, datatype_iri: String) -> Self { OtherDataValue(lexical_value, datatype_iri) } } diff --git a/nemo-physical/src/datavalues/tuple_datavalue.rs b/nemo-physical/src/datavalues/tuple_datavalue.rs index 42b8b0518..5f4342914 100644 --- a/nemo-physical/src/datavalues/tuple_datavalue.rs +++ b/nemo-physical/src/datavalues/tuple_datavalue.rs @@ -14,7 +14,6 @@ pub struct TupleDataValue { impl TupleDataValue { /// Constructor. - #[allow(dead_code)] pub fn new>( label: Option, values: T, diff --git a/nemo/src/io/formats/json/datavalues/map.rs b/nemo/src/io/formats/json/datavalues/map.rs index 09059d632..28ee69aef 100644 --- a/nemo/src/io/formats/json/datavalues/map.rs +++ b/nemo/src/io/formats/json/datavalues/map.rs @@ -1,21 +1,35 @@ pub(crate) mod default { + use nemo_physical::datavalues::{DataValue, MapDataValue}; - use serde::{de::Visitor, Deserializer, Serializer}; + use serde::{de::Visitor, ser::SerializeMap, Deserializer, Serializer}; + type AnyWrapper = crate::io::formats::json::variants::default::JsonAnyDataValue; struct MapVisitor {} impl<'de> Visitor<'de> for MapVisitor { type Value = MapDataValue; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "a boolean") + write!(formatter, "a map") } - fn visit_bool(self, v: bool) -> Result + fn visit_map(self, mut map: A) -> Result where - E: serde::de::Error, + A: serde::de::MapAccess<'de>, { - Ok(MapDataValue::new(v)) + let mut entries = + Vec::<(AnyWrapper, AnyWrapper)>::with_capacity(map.size_hint().unwrap_or_default()); + + while let Some(entry) = map.next_entry()? { + entries.push(entry) + } + + Ok(MapDataValue::new( + None, + entries + .into_iter() + .map(|(key, value)| (key.into(), value.into())), + )) } } @@ -23,13 +37,26 @@ pub(crate) mod default { where D: Deserializer<'de>, { - deserializer.deserialize_string(MapVisitor {}) + deserializer.deserialize_map(MapVisitor {}) } pub(crate) fn serialize(value: &MapDataValue, serializer: S) -> Result where S: Serializer, { - serializer.serialize_bool(value.to_boolean_unchecked()) + let mut map = serializer.serialize_map(value.length())?; + + assert!(value.label().is_none(), "cannot serialize maps with labels"); + + if let Some(entries) = value.map_items() { + for (key, value) in entries { + map.serialize_entry( + &AnyWrapper::from(key.clone()), + &AnyWrapper::from(value.clone()), + )?; + } + } + + map.end() } } diff --git a/nemo/src/io/formats/json/datavalues/other.rs b/nemo/src/io/formats/json/datavalues/other.rs index 11b0a8434..a54c4de5e 100644 --- a/nemo/src/io/formats/json/datavalues/other.rs +++ b/nemo/src/io/formats/json/datavalues/other.rs @@ -1,12 +1,62 @@ pub(crate) mod default { - use nemo_physical::datavalues::DataValue; - use serde::{Deserializer, Serializer}; + use nemo_physical::datavalues::{DataValue, OtherDataValue}; + use nom::InputLength; + use serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }; - pub(crate) fn deserialize<'de, T, D>(_deserializer: D) -> Result + use crate::parser::{ + ast::{ + expression::basic::rdf_literal::RdfLiteral, tag::structure::StructureTagKind, + ProgramAST, + }, + input::ParserInput, + }; + + struct OtherVisitor {} + + impl<'de> Visitor<'de> for OtherVisitor { + type Value = OtherDataValue; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "an RDF literal with datatype") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + self.visit_string(v.to_string()) + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + let (rest, value) = RdfLiteral::parse(ParserInput::stateless(&v)) + .map_err(|err| Error::custom(err.to_string()))?; + + if rest.input_len() > 0 { + Err(Error::custom(format!( + "unexpected `{rest}` after language-tagged string" + ))) + } else if !matches!(value.tag().kind(), StructureTagKind::Iri(_)) { + Err(Error::custom("expected literal datatype to be an IRI")) + } else { + Ok(OtherDataValue::new( + value.content(), + value.tag().to_string(), + )) + } + } + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - todo!() + deserializer.deserialize_string(OtherVisitor {}) } pub(crate) fn serialize(value: &T, serializer: S) -> Result diff --git a/nemo/src/io/formats/json/datavalues/tuple.rs b/nemo/src/io/formats/json/datavalues/tuple.rs index 1451eb5cc..9169f0c60 100644 --- a/nemo/src/io/formats/json/datavalues/tuple.rs +++ b/nemo/src/io/formats/json/datavalues/tuple.rs @@ -1,21 +1,31 @@ pub(crate) mod default { use nemo_physical::datavalues::{DataValue, TupleDataValue}; - use serde::{de::Visitor, Deserializer, Serializer}; + use serde::{de::Visitor, ser::SerializeSeq, Deserializer, Serializer}; + type AnyWrapper = crate::io::formats::json::variants::default::JsonAnyDataValue; struct TupleVisitor {} impl<'de> Visitor<'de> for TupleVisitor { type Value = TupleDataValue; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "a boolean") + write!(formatter, "a list") } - fn visit_bool(self, v: bool) -> Result + fn visit_seq(self, mut seq: A) -> Result where - E: serde::de::Error, + A: serde::de::SeqAccess<'de>, { - Ok(TupleDataValue::new(v)) + let mut entries = Vec::::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(entry) = seq.next_element()? { + entries.push(entry); + } + + Ok(TupleDataValue::new( + None, + entries.into_iter().map(|entry| entry.into()), + )) } } @@ -23,13 +33,26 @@ pub(crate) mod default { where D: Deserializer<'de>, { - deserializer.deserialize_string(TupleVisitor {}) + deserializer.deserialize_seq(TupleVisitor {}) } pub(crate) fn serialize(value: &TupleDataValue, serializer: S) -> Result where S: Serializer, { - serializer.serialize_bool(value.to_boolean_unchecked()) + assert!( + value.label().is_none(), + "cannot serialize tuples with labels" + ); + let length = value.length().expect("tuples always have a fixed length"); + let mut seq = serializer.serialize_seq(Some(length))?; + + for idx in 0..length { + seq.serialize_element(&AnyWrapper::from( + value.tuple_element_unchecked(idx).clone(), + ))?; + } + + seq.end() } } diff --git a/nemo/src/io/formats/json/variants.rs b/nemo/src/io/formats/json/variants.rs index cd3428ddb..75946ceeb 100644 --- a/nemo/src/io/formats/json/variants.rs +++ b/nemo/src/io/formats/json/variants.rs @@ -22,6 +22,7 @@ macro_rules! json_variant { use crate::io::formats::json::datavalues; #[derive(Deserialize, Serialize)] + #[serde(untagged)] pub(crate) enum JsonAnyDataValue { $( #[serde(with = $policy)] diff --git a/resources/testcases/data-formats/json/works.json b/resources/testcases/data-formats/json/works.json index 86d1e057b..eff1c9af8 100644 --- a/resources/testcases/data-formats/json/works.json +++ b/resources/testcases/data-formats/json/works.json @@ -1 +1,94 @@ -{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":3969,"items":[{"indexed":{"date-parts":[[2022,3,30]],"date-time":"2022-03-30T02:49:53Z","timestamp":1648608593454},"publisher-location":"New York, NY","reference-count":18,"publisher":"Springer New York","license":[{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"tdm","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"vor","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"tdm","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"vor","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"published-print":{"date-parts":[[2020]]},"DOI":"10.1007\/978-1-4614-8678-7_119","type":"book-chapter","created":{"date-parts":[[2020,11,4]],"date-time":"2020-11-04T08:03:17Z","timestamp":1604476997000},"page":"493-496","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":0,"title":["NEMO Disease Spectrum Including NEMO-Deleted Exon 5 Autoinflammatory Syndrome (NDAS) and NEMO-Delta C-Terminus (NEMO-DCT)"],"prefix":"10.1007","author":[{"given":"Eric P.","family":"Hanson","sequence":"first","affiliation":[]}],"member":"297","published-online":{"date-parts":[[2020,11,5]]},"reference":[{"key":"119_CR141","doi-asserted-by":"publisher","first-page":"1040","DOI":"10.1016\/j.jaci.2016.08.039","volume":"139","author":"S Chandrakasan","year":"2017","unstructured":"Chandrakasan S, et al. Outcome of patients with NEMO deficiency following allogeneic hematopoietic cell transplant. J Allergy Clin Immunol. 2017;139:1040\u20133.","journal-title":"J Allergy Clin Immunol"},{"key":"119_CR142","doi-asserted-by":"publisher","first-page":"124","DOI":"10.1016\/j.clim.2009.03.514","volume":"132","author":"LE Cheng","year":"2009","unstructured":"Cheng LE, et al. Persistent systemic inflammation and atypical enterocolitis in patients with NEMO syndrome. Clin Immunol. 2009;132:124\u201331.","journal-title":"Clin Immunol"},{"key":"119_CR143","unstructured":"de Jesus A, et al. Distinct interferon signatures and cytokine patterns define additional systemic autoinflammatory diseases. J Clin Inv. 2019; in press."},{"key":"119_CR144","doi-asserted-by":"publisher","first-page":"277","DOI":"10.1038\/85837","volume":"27","author":"R Doffinger","year":"2001","unstructured":"Doffinger R, et al. X-linked anhidrotic ectodermal dysplasia with immunodeficiency is caused by impaired NF-kappaB signaling. Nat Genet. 2001;27:277\u201385.","journal-title":"Nat Genet"},{"key":"119_CR145","doi-asserted-by":"publisher","first-page":"1169.e1116","DOI":"10.1016\/j.jaci.2008.08.018","volume":"122","author":"EP Hanson","year":"2008","unstructured":"Hanson EP, et al. Hypomorphic nuclear factor-kappaB essential modulator mutation database and reconstitution system identifies phenotypic and immunologic diversity. J Allergy Clin Immunol. 2008a;122:1169.e1116\u201377.e1116.","journal-title":"J Allergy Clin Immunol"},{"key":"119_CR146","doi-asserted-by":"publisher","first-page":"1169.e1116","DOI":"10.1016\/j.jaci.2008.08.018","volume":"122","author":"E Hanson","year":"2008","unstructured":"Hanson E, et al. Hypomorphic nuclear factor-kappaB essential modulator mutation database and reconstitution system identifies phenotypic and immunologic diversity. J Allergy Clin Immunol. 2008b;122:1169.e1116\u201377.e1116.","journal-title":"J Allergy Clin Immunol"},{"key":"119_CR147","doi-asserted-by":"publisher","first-page":"61","DOI":"10.3389\/fimmu.2011.00061","volume":"2","author":"MD Keller","year":"2011","unstructured":"Keller MD, et al. Hypohidrotic ectodermal dysplasia and immunodeficiency with coincident NEMO and EDA mutations. Front Immunol. 2011;2:61.","journal-title":"Front Immunol"},{"key":"119_CR148","doi-asserted-by":"publisher","first-page":"969","DOI":"10.1016\/S1097-2765(00)80262-2","volume":"5","author":"C Makris","year":"2000","unstructured":"Makris C, et al. Female mice heterozygous for IKK gamma\/NEMO deficiencies develop a dermatopathy similar to the human X-linked disorder incontinentia pigmenti. Mol Cell. 2000;5:969\u201379.","journal-title":"Mol Cell"},{"key":"119_CR149","doi-asserted-by":"publisher","first-page":"342","DOI":"10.1001\/archderm.144.3.342","volume":"144","author":"A Mancini","year":"2008","unstructured":"Mancini A, Lawley L, Uzel G. X-linked ectodermal dysplasia with immunodeficiency caused by NEMO mutation: early recognition and diagnosis. Arch Dermatol. 2008a;144:342\u20136.","journal-title":"Arch Dermatol"},{"key":"119_CR1410","doi-asserted-by":"publisher","first-page":"342","DOI":"10.1001\/archderm.144.3.342","volume":"144","author":"AJ Mancini","year":"2008","unstructured":"Mancini AJ, Lawley LP, Uzel G. X-linked ectodermal dysplasia with immunodeficiency caused by NEMO mutation: early recognition and diagnosis. Arch Dermatol. 2008b;144:342\u20136.","journal-title":"Arch Dermatol"},{"key":"119_CR1411","doi-asserted-by":"publisher","first-page":"1456","DOI":"10.1182\/blood-2017-03-771600","volume":"130","author":"C Miot","year":"2017","unstructured":"Miot C, et al. Hematopoietic stem cell transplantation in 29 patients hemizygous for hypomorphic IKBKG\/NEMO mutations. Blood. 2017;130:1456.","journal-title":"Blood"},{"key":"119_CR1412","doi-asserted-by":"publisher","first-page":"531","DOI":"10.1093\/hmg\/ddi470","volume":"15","author":"A Nenci","year":"2006","unstructured":"Nenci A, et al. Skin lesion development in a mouse model of incontinentia pigmenti is triggered by NEMO deficiency in epidermal keratinocytes and requires TNF signaling. Hum Mol Genet. 2006;15:531\u201342.","journal-title":"Hum Mol Genet"},{"key":"119_CR1413","doi-asserted-by":"publisher","first-page":"557","DOI":"10.1038\/nature05698","volume":"446","author":"A Nenci","year":"2007","unstructured":"Nenci A, et al. Epithelial NEMO links innate immunity to chronic intestinal inflammation. Nature. 2007;446:557\u201361.","journal-title":"Nature"},{"key":"119_CR1414","doi-asserted-by":"publisher","first-page":"e1049","DOI":"10.1542\/peds.2005-2062","volume":"117","author":"JM Pachlopnik Schmid","year":"2006","unstructured":"Pachlopnik Schmid JM, et al. Transient hemophagocytosis with deficient cellular cytotoxicity, monoclonal immunoglobulin M gammopathy, increased T-cell numbers, and hypomorphic NEMO mutation. Pediatrics. 2006;117:e1049\u201356.","journal-title":"Pediatrics"},{"key":"119_CR1415","doi-asserted-by":"publisher","first-page":"575","DOI":"10.1111\/j.1399-0004.2010.01432.x","volume":"78","author":"H Takada","year":"2010","unstructured":"Takada H, Nomura A, Ishimura M, Ichiyama M, Ohga S, Hara T. NEMO mutation as a cause of familial occurrence of Beh\u00e7et\u2019s disease in female patients. Clin Genet. 2010;78:575\u20139.","journal-title":"Clin Genet"},{"key":"119_CR1416","doi-asserted-by":"publisher","first-page":"801","DOI":"10.1038\/sj.bmt.1705658","volume":"39","author":"C Tono","year":"2007","unstructured":"Tono C, et al. Correction of immunodeficiency associated with NEMO mutation by umbilical cord blood transplantation using a reduced-intensity conditioning regimen. Bone Marrow Transpl. 2007;39:801\u20134.","journal-title":"Bone Marrow Transpl"},{"key":"119_CR1417","doi-asserted-by":"publisher","first-page":"Ab113-Ab113","DOI":"10.1016\/j.jaci.2016.12.364","volume":"139","author":"TR Yates","year":"2017","unstructured":"Yates TR, Wright BL, Bauer CS, Difficulty Finding NEMO. Functional pathways to sequencing. J Allergy Clin Immunol. 2017;139:Ab113-Ab113.","journal-title":"J Allergy Clin Immunol"},{"key":"119_CR1418","doi-asserted-by":"publisher","first-page":"1612","DOI":"10.1073\/pnas.1518163113","volume":"113","author":"J Zilberman-Rudenko","year":"2016","unstructured":"Zilberman-Rudenko J, et al. Recruitment of A20 by the C-terminal domain of NEMO suppresses NF-kappaB activation and autoinflammatory disease. Proc Natl Acad Sci U S A. 2016;113:1612\u20137.","journal-title":"Proc Natl Acad Sci U S A"}],"container-title":["Encyclopedia of Medical Immunology"],"link":[{"URL":"http:\/\/link.springer.com\/content\/pdf\/10.1007\/978-1-4614-8678-7_119","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2020,11,7]],"date-time":"2020-11-07T00:12:07Z","timestamp":1604707927000},"score":18.338985,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1007\/978-1-4614-8678-7_119"}},"issued":{"date-parts":[[2020]]},"references-count":18,"URL":"http:\/\/dx.doi.org\/10.1007\/978-1-4614-8678-7_119","published":{"date-parts":[[2020]]},"assertion":[{"value":"5 November 2020","order":1,"name":"first_online","label":"First Online","group":{"name":"ChapterHistory","label":"Chapter History"}}]},{"indexed":{"date-parts":[[2022,12,14]],"date-time":"2022-12-14T06:26:35Z","timestamp":1670999195512},"publisher":"Publications Office of the European Union","award-start":{"date-parts":[[2016,10,1]]},"award":"713794","DOI":"10.3030\/713794","type":"grant","created":{"date-parts":[[2022,12,12]],"date-time":"2022-12-12T13:14:59Z","timestamp":1670850899000},"source":"Crossref","prefix":"10.3030","member":"4854","project":[{"project-title":[{"title":"NeMo : Hyper-Network for electroMobility","language":"en"},{"title":"NeMo","language":"en"}],"project-description":[{"description":"Electromobility is a major factor towards transport decarbonisation. However a number of challenges (limited charging options, lack of interoperability, absence of a unified identification\/payment process, energy grid overload, expensive charging tariffs) limit the potential for interoperable and seamless electromobility services to a wider of actors and geographic area, hindering electromobility adoption. These challenges stem from lack of standardisation in electromobility data and services. NeMo addresses all issues through a pan-European eRoaming Hyper-Network that allows seamless and interoperable use of electromobility services throughout Europe. In addition it provides an Open Cloud Marketplace, where third parties can provide services (B2B2C) aiming to increase EV attractiveness. The NeMo Hyper-Network is a distributed environment with open architecture based on standardised interfaces, in which all electromobility actors, physical (i.e. CPs, grids, EVs) or digital (i.e. CPOs, DSOs, etc.), can connect and interact seamlessly, exchange data and provide more elaborate electromobility ICT services in a fully integrated and interoperable way both B2B and B2C. The connection will be based on dynamic translation of data and services interfaces according to needs of the specific scenarios and involved stakeholders. NeMo is not just another proprietary platform for electromobility but a full open eco-system allowing continuous and uninterrupted provision of data and services. NeMo will raise awareness, liaise with standardisation bodies and contribute to the evolution of protocols and standards by developing public Common Information Models which incorporate all existing electromobility related standards and constantly update them to reflect standards evolution. NeMo will also propose sustainable business models for all electromobility actors opening new opportunities for SMEs and EU Industry.","language":"en"}],"award-amount":{"amount":7836827.0,"currency":"EUR"},"award-start":{"date-parts":[[2016,10,1]]},"award-end":{"date-parts":[[2019,9,30]]},"award-planned-end":{"date-parts":[[2019,9,30]]},"funding":[{"type":"grant","scheme":"GV-8-2015","award-amount":{"amount":7836827.0,"currency":"EUR","percentage":100},"funder":{"name":"H2020 Transport","id":[{"id":"10.13039\/100010680","id-type":"DOI","asserted-by":"publisher"}]}}]}],"deposited":{"date-parts":[[2022,12,13]],"date-time":"2022-12-13T12:58:23Z","timestamp":1670936303000},"score":18.136581,"resource":{"primary":{"URL":"https:\/\/cordis.europa.eu\/project\/id\/713794"}},"issued":{"date-parts":[[2016,10,1]]},"URL":"http:\/\/dx.doi.org\/10.3030\/713794"},{"indexed":{"date-parts":[[2022,4,3]],"date-time":"2022-04-03T04:37:28Z","timestamp":1648960648738},"publisher-location":"New York, NY","reference-count":18,"publisher":"Springer New York","isbn-type":[{"value":"9781461492092","type":"print"},{"value":"9781461492092","type":"electronic"}],"license":[{"start":{"date-parts":[[2019,12,13]],"date-time":"2019-12-13T00:00:00Z","timestamp":1576195200000},"content-version":"tdm","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2019,12,13]],"date-time":"2019-12-13T00:00:00Z","timestamp":1576195200000},"content-version":"vor","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"published-print":{"date-parts":[[2020]]},"DOI":"10.1007\/978-1-4614-9209-2_119-1","type":"book-chapter","created":{"date-parts":[[2020,6,3]],"date-time":"2020-06-03T13:32:06Z","timestamp":1591191126000},"page":"1-4","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":0,"title":["NEMO disease spectrum including NEMO-deleted exon 5 autoinflammatory syndrome (NDAS) and NEMO-Delta C-terminus (NEMO-DCT)"],"prefix":"10.1007","author":[{"given":"Eric P.","family":"Hanson","sequence":"first","affiliation":[]}],"member":"297","published-online":{"date-parts":[[2019,12,13]]},"reference":[{"key":"119-1_CR1","doi-asserted-by":"publisher","first-page":"1040","DOI":"10.1016\/j.jaci.2016.08.039","volume":"139","author":"S Chandrakasan","year":"2017","unstructured":"Chandrakasan S, et al. Outcome of patients with NEMO deficiency following allogeneic hematopoietic cell transplant. J Allergy Clin Immunol. 2017;139:1040\u20133.","journal-title":"J Allergy Clin Immunol"},{"key":"119-1_CR2","doi-asserted-by":"publisher","first-page":"124","DOI":"10.1016\/j.clim.2009.03.514","volume":"132","author":"LE Cheng","year":"2009","unstructured":"Cheng LE, et al. Persistent systemic inflammation and atypical enterocolitis in patients with NEMO syndrome. Clin Immunol. 2009;132:124\u201331.","journal-title":"Clin Immunol"},{"key":"119-1_CR18","unstructured":"de Jesus A, et al. Distinct interferon signatures and cytokine patterns define additional systemic autoinflammatory diseases. J Clin Inv. 2019; in press."},{"key":"119-1_CR3","doi-asserted-by":"publisher","first-page":"277","DOI":"10.1038\/85837","volume":"27","author":"R Doffinger","year":"2001","unstructured":"Doffinger R, et al. X-linked anhidrotic ectodermal dysplasia with immunodeficiency is caused by impaired NF-kappaB signaling. Nat Genet. 2001;27:277\u201385.","journal-title":"Nat Genet"},{"key":"119-1_CR4","doi-asserted-by":"publisher","first-page":"1169.e1116","DOI":"10.1016\/j.jaci.2008.08.018","volume":"122","author":"EP Hanson","year":"2008","unstructured":"Hanson EP, et al. Hypomorphic nuclear factor-kappaB essential modulator mutation database and reconstitution system identifies phenotypic and immunologic diversity. J Allergy Clin Immunol. 2008a;122:1169.e1116\u201377.e1116.","journal-title":"J Allergy Clin Immunol"},{"key":"119-1_CR5","doi-asserted-by":"publisher","first-page":"1169.e1116","DOI":"10.1016\/j.jaci.2008.08.018","volume":"122","author":"E Hanson","year":"2008","unstructured":"Hanson E, et al. Hypomorphic nuclear factor-kappaB essential modulator mutation database and reconstitution system identifies phenotypic and immunologic diversity. J Allergy Clin Immunol. 2008b;122:1169.e1116\u201377.e1116.","journal-title":"J Allergy Clin Immunol"},{"key":"119-1_CR6","doi-asserted-by":"publisher","first-page":"61","DOI":"10.3389\/fimmu.2011.00061","volume":"2","author":"MD Keller","year":"2011","unstructured":"Keller MD, et al. Hypohidrotic ectodermal dysplasia and immunodeficiency with coincident NEMO and EDA mutations. Front Immunol. 2011;2:61.","journal-title":"Front Immunol"},{"key":"119-1_CR7","doi-asserted-by":"publisher","first-page":"969","DOI":"10.1016\/S1097-2765(00)80262-2","volume":"5","author":"C Makris","year":"2000","unstructured":"Makris C, et al. Female mice heterozygous for IKK gamma\/NEMO deficiencies develop a dermatopathy similar to the human X-linked disorder incontinentia pigmenti. Mol Cell. 2000;5:969\u201379.","journal-title":"Mol Cell"},{"key":"119-1_CR8","doi-asserted-by":"publisher","first-page":"342","DOI":"10.1001\/archderm.144.3.342","volume":"144","author":"A Mancini","year":"2008","unstructured":"Mancini A, Lawley L, Uzel G. X-linked ectodermal dysplasia with immunodeficiency caused by NEMO mutation: early recognition and diagnosis. Arch Dermatol. 2008a;144:342\u20136.","journal-title":"Arch Dermatol"},{"key":"119-1_CR9","doi-asserted-by":"publisher","first-page":"342","DOI":"10.1001\/archderm.144.3.342","volume":"144","author":"AJ Mancini","year":"2008","unstructured":"Mancini AJ, Lawley LP, Uzel G. X-linked ectodermal dysplasia with immunodeficiency caused by NEMO mutation: early recognition and diagnosis. Arch Dermatol. 2008b;144:342\u20136.","journal-title":"Arch Dermatol"},{"key":"119-1_CR10","doi-asserted-by":"publisher","first-page":"1456","DOI":"10.1182\/blood-2017-03-771600","volume":"130","author":"C Miot","year":"2017","unstructured":"Miot C, et al. Hematopoietic stem cell transplantation in 29 patients hemizygous for hypomorphic IKBKG\/NEMO mutations. Blood. 2017;130:1456.","journal-title":"Blood"},{"key":"119-1_CR11","doi-asserted-by":"publisher","first-page":"531","DOI":"10.1093\/hmg\/ddi470","volume":"15","author":"A Nenci","year":"2006","unstructured":"Nenci A, et al. Skin lesion development in a mouse model of incontinentia pigmenti is triggered by NEMO deficiency in epidermal keratinocytes and requires TNF signaling. Hum Mol Genet. 2006;15:531\u201342.","journal-title":"Hum Mol Genet"},{"key":"119-1_CR12","doi-asserted-by":"publisher","first-page":"557","DOI":"10.1038\/nature05698","volume":"446","author":"A Nenci","year":"2007","unstructured":"Nenci A, et al. Epithelial NEMO links innate immunity to chronic intestinal inflammation. Nature. 2007;446:557\u201361.","journal-title":"Nature"},{"key":"119-1_CR13","doi-asserted-by":"publisher","first-page":"e1049","DOI":"10.1542\/peds.2005-2062","volume":"117","author":"JM Pachlopnik Schmid","year":"2006","unstructured":"Pachlopnik Schmid JM, et al. Transient hemophagocytosis with deficient cellular cytotoxicity, monoclonal immunoglobulin M gammopathy, increased T-cell numbers, and hypomorphic NEMO mutation. Pediatrics. 2006;117:e1049\u201356.","journal-title":"Pediatrics"},{"key":"119-1_CR14","doi-asserted-by":"publisher","first-page":"575","DOI":"10.1111\/j.1399-0004.2010.01432.x","volume":"78","author":"H Takada","year":"2010","unstructured":"Takada H, Nomura A, Ishimura M, Ichiyama M, Ohga S, Hara T. NEMO mutation as a cause of familial occurrence of Beh\u00e7et\u2019s disease in female patients. Clin Genet. 2010;78:575\u20139.","journal-title":"Clin Genet"},{"key":"119-1_CR15","doi-asserted-by":"publisher","first-page":"801","DOI":"10.1038\/sj.bmt.1705658","volume":"39","author":"C Tono","year":"2007","unstructured":"Tono C, et al. Correction of immunodeficiency associated with NEMO mutation by umbilical cord blood transplantation using a reduced-intensity conditioning regimen. Bone Marrow Transpl. 2007;39:801\u20134.","journal-title":"Bone Marrow Transpl"},{"key":"119-1_CR16","doi-asserted-by":"publisher","first-page":"Ab113-Ab113","DOI":"10.1016\/j.jaci.2016.12.364","volume":"139","author":"TR Yates","year":"2017","unstructured":"Yates TR, Wright BL, Bauer CS, Difficulty Finding NEMO. Functional pathways to sequencing. J Allergy Clin Immunol. 2017;139:Ab113-Ab113.","journal-title":"J Allergy Clin Immunol"},{"key":"119-1_CR17","doi-asserted-by":"publisher","first-page":"1612","DOI":"10.1073\/pnas.1518163113","volume":"113","author":"J Zilberman-Rudenko","year":"2016","unstructured":"Zilberman-Rudenko J, et al. Recruitment of A20 by the C-terminal domain of NEMO suppresses NF-kappaB activation and autoinflammatory disease. Proc Natl Acad Sci U S A. 2016;113:1612\u20137.","journal-title":"Proc Natl Acad Sci U S A"}],"container-title":["Encyclopedia of Medical Immunology"],"link":[{"URL":"http:\/\/link.springer.com\/content\/pdf\/10.1007\/978-1-4614-9209-2_119-1","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2020,6,3]],"date-time":"2020-06-03T13:32:11Z","timestamp":1591191131000},"score":18.000843,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1007\/978-1-4614-9209-2_119-1"}},"issued":{"date-parts":[[2019,12,13]]},"ISBN":["9781461492092","9781461492092"],"references-count":18,"URL":"http:\/\/dx.doi.org\/10.1007\/978-1-4614-9209-2_119-1","published":{"date-parts":[[2019,12,13]]},"assertion":[{"value":"24 November 2019, 00:00:00","order":1,"name":"received","label":"Received","group":{"name":"ChapterHistory","label":"Chapter History"}},{"value":"24 November 2019, 00:00:00","order":2,"name":"accepted","label":"Accepted","group":{"name":"ChapterHistory","label":"Chapter History"}},{"value":"13 December 2019","order":3,"name":"first_online","label":"First Online","group":{"name":"ChapterHistory","label":"Chapter History"}}]},{"indexed":{"date-parts":[[2023,8,1]],"date-time":"2023-08-01T04:28:20Z","timestamp":1690864100505},"posted":{"date-parts":[[2023,6,7]]},"reference-count":0,"publisher":"ZappyLab, Inc.","license":[{"start":{"date-parts":[[2023,6,7]],"date-time":"2023-06-07T00:00:00Z","timestamp":1686096000000},"content-version":"unspecified","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0\/"}],"content-domain":{"domain":[],"crossmark-restriction":false},"abstract":"

This protocol describes how to express and purify human NEMO (IKK-\u03b3) tagged N-terminally with GST and EGFP. The expression is performed with E. coli Rosetta pLysS cells. The protein is purified via a GST batch purification and gel filtration (SEC). <\/p>","DOI":"10.17504\/protocols.io.261gedpojv47\/v1","type":"posted-content","created":{"date-parts":[[2023,7,31]],"date-time":"2023-07-31T14:55:35Z","timestamp":1690815335000},"source":"Crossref","is-referenced-by-count":0,"title":["EXPRESSION AND PURIFICATION OF HUMAN NEMO (GST-GFP-NEMO) v1"],"prefix":"10.17504","author":[{"ORCID":"http:\/\/orcid.org\/0000-0003-1806-737X","authenticated-orcid":false,"given":"Elisabeth","family":"Holzer","sequence":"first","affiliation":[]}],"member":"7078","deposited":{"date-parts":[[2023,7,31]],"date-time":"2023-07-31T14:55:36Z","timestamp":1690815336000},"score":17.67471,"resource":{"primary":{"URL":"https:\/\/www.protocols.io\/view\/expression-and-purification-of-human-nemo-gst-gfp-cva6w2he"}},"issued":{"date-parts":[[2023,6,7]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.17504\/protocols.io.261gedpojv47\/v1","published":{"date-parts":[[2023,6,7]]},"subtype":"preprint"},{"indexed":{"date-parts":[[2022,3,30]],"date-time":"2022-03-30T15:09:00Z","timestamp":1648652940644},"publisher-location":"New York, NY","reference-count":0,"publisher":"Springer New York","license":[{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"tdm","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"vor","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"tdm","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"},{"start":{"date-parts":[[2020,1,1]],"date-time":"2020-01-01T00:00:00Z","timestamp":1577836800000},"content-version":"vor","delay-in-days":0,"URL":"http:\/\/www.springer.com\/tdm"}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"published-print":{"date-parts":[[2020]]},"DOI":"10.1007\/978-1-4614-8678-7_300234","type":"book-chapter","created":{"date-parts":[[2020,11,4]],"date-time":"2020-11-04T08:03:17Z","timestamp":1604476997000},"page":"493-493","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":0,"title":["NEMO Delta C-Terminus (NEMO-DCT)"],"prefix":"10.1007","member":"297","published-online":{"date-parts":[[2020,11,5]]},"container-title":["Encyclopedia of Medical Immunology"],"link":[{"URL":"http:\/\/link.springer.com\/content\/pdf\/10.1007\/978-1-4614-8678-7_300234","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2020,11,7]],"date-time":"2020-11-07T00:11:53Z","timestamp":1604707913000},"score":17.189198,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1007\/978-1-4614-8678-7_300234"}},"issued":{"date-parts":[[2020]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1007\/978-1-4614-8678-7_300234","published":{"date-parts":[[2020]]},"assertion":[{"value":"5 November 2020","order":1,"name":"first_online","label":"First Online","group":{"name":"ChapterHistory","label":"Chapter History"}}]},{"indexed":{"date-parts":[[2023,4,20]],"date-time":"2023-04-20T04:47:25Z","timestamp":1681966045772},"reference-count":0,"publisher":"AIP Publishing LLC","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2014]]},"DOI":"10.1063\/1.4902797","type":"proceedings-article","created":{"date-parts":[[2014,11,19]],"date-time":"2014-11-19T13:32:29Z","timestamp":1416403949000},"source":"Crossref","is-referenced-by-count":0,"title":["Trigger study for NEMO Phase 2"],"prefix":"10.1063","author":[{"given":"B.","family":"Bouhadef","sequence":"first","affiliation":[]},{"family":"NEMO collaboration","sequence":"additional","affiliation":[]}],"member":"317","event":{"name":"APPLICATIONS OF MATHEMATICS IN ENGINEERING AND ECONOMICS (AMEE'14)","location":"Sozopol, Bulgaria"},"container-title":["AIP Conference Proceedings"],"deposited":{"date-parts":[[2023,4,19]],"date-time":"2023-04-19T22:07:45Z","timestamp":1681942065000},"score":17.125168,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/aip\/acp\/article\/1630\/1\/163-166\/882622"}},"issued":{"date-parts":[[2014]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1063\/1.4902797","ISSN":["0094-243X"],"issn-type":[{"value":"0094-243X","type":"print"}],"published":{"date-parts":[[2014]]}},{"indexed":{"date-parts":[[2023,6,30]],"date-time":"2023-06-30T06:50:48Z","timestamp":1688107848806},"reference-count":0,"publisher":"AIP","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2007]]},"DOI":"10.1063\/1.2722065","type":"proceedings-article","created":{"date-parts":[[2007,4,6]],"date-time":"2007-04-06T22:06:33Z","timestamp":1175897193000},"source":"Crossref","is-referenced-by-count":7,"title":["Radon reduction and radon monitoring in the NEMO experiment"],"prefix":"10.1063","author":[{"given":"A.","family":"Nachab","sequence":"first","affiliation":[]},{"name":"NEMO collaboration","sequence":"additional","affiliation":[]}],"member":"317","event":{"name":"TOPICAL WORKSHOP ON LOW RADIOACTIVITY TECHNIQUES: LRT 2006","location":"Aussois (France)"},"container-title":["AIP Conference Proceedings"],"deposited":{"date-parts":[[2023,4,20]],"date-time":"2023-04-20T11:22:31Z","timestamp":1681989751000},"score":16.822826,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/aip\/acp\/article\/897\/1\/35-38\/974048"}},"issued":{"date-parts":[[2007]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1063\/1.2722065","ISSN":["0094-243X"],"issn-type":[{"value":"0094-243X","type":"print"}],"published":{"date-parts":[[2007]]}},{"indexed":{"date-parts":[[2022,3,29]],"date-time":"2022-03-29T03:31:29Z","timestamp":1648524689413},"publisher-location":"Berlin\/Heidelberg","reference-count":0,"publisher":"Springer-Verlag","content-domain":{"domain":[],"crossmark-restriction":false},"DOI":"10.1007\/springerreference_36072","type":"reference-entry","created":{"date-parts":[[2011,8,24]],"date-time":"2011-08-24T11:04:09Z","timestamp":1314183849000},"source":"Crossref","is-referenced-by-count":0,"title":["Nemo Like Kinase"],"prefix":"10.1007","member":"297","container-title":["SpringerReference"],"deposited":{"date-parts":[[2011,8,24]],"date-time":"2011-08-24T11:12:45Z","timestamp":1314184365000},"score":16.673275,"resource":{"primary":{"URL":"http:\/\/www.springerreference.com\/index\/doi\/10.1007\/SpringerReference_36072"}},"issued":{"date-parts":[[null]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1007\/springerreference_36072"},{"indexed":{"date-parts":[[2023,4,20]],"date-time":"2023-04-20T04:28:26Z","timestamp":1681964906593},"reference-count":0,"publisher":"AIP","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2010]]},"DOI":"10.1063\/1.3362602","type":"proceedings-article","created":{"date-parts":[[2010,3,8]],"date-time":"2010-03-08T15:25:34Z","timestamp":1268061934000},"source":"Crossref","is-referenced-by-count":0,"title":["Status of NEMO Project"],"prefix":"10.1063","author":[{"given":"S.","family":"Viola","sequence":"first","affiliation":[]},{"given":"Claudi","family":"Spitaleri","sequence":"additional","affiliation":[]},{"given":"Claus","family":"Rolfs","sequence":"additional","affiliation":[]},{"given":"Rosario G.","family":"Pizzone","sequence":"additional","affiliation":[]},{"name":"NEMO Collaboration","sequence":"additional","affiliation":[]}],"member":"317","event":{"name":"FIFTH EUROPEAN SUMMER SCHOOL ON EXPERIMENTAL NUCLEAR ASTROPHYSICS","location":"Santa Tecla, Sicily, (Italy)"},"container-title":["AIP Conference Proceedings"],"deposited":{"date-parts":[[2023,4,19]],"date-time":"2023-04-19T23:59:41Z","timestamp":1681948781000},"score":16.544664,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/aip\/acp\/article\/1213\/1\/249-251\/835267"}},"issued":{"date-parts":[[2010]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1063\/1.3362602","ISSN":["0094-243X"],"issn-type":[{"value":"0094-243X","type":"print"}],"published":{"date-parts":[[2010]]}},{"indexed":{"date-parts":[[2022,4,1]],"date-time":"2022-04-01T17:45:50Z","timestamp":1648835150320},"reference-count":0,"publisher":"Tectum \u2013 ein Verlag in der Nomos Verlagsgesellschaft","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2021]]},"DOI":"10.5771\/9783828876873-57","type":"book-chapter","created":{"date-parts":[[2021,3,3]],"date-time":"2021-03-03T12:02:46Z","timestamp":1614772966000},"page":"57-62","source":"Crossref","is-referenced-by-count":0,"title":["C. Geltungsreichweite: Die Ausstrahlungswirkung von nemo-tenetur"],"prefix":"10.5771","author":[{"given":"Tobias Alexander","family":"Knippel","sequence":"first","affiliation":[]}],"member":"3821","container-title":["Das nemo-tenetur-Prinzip bei au\u00dferstrafrechtlicher Pflichterf\u00fcllung"],"deposited":{"date-parts":[[2021,3,3]],"date-time":"2021-03-03T12:03:12Z","timestamp":1614772992000},"score":16.436691,"resource":{"primary":{"URL":"https:\/\/www.tectum-elibrary.de\/index.php?doi=10.5771\/9783828876873-57"}},"issued":{"date-parts":[[2021]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.5771\/9783828876873-57","published":{"date-parts":[[2021]]}},{"indexed":{"date-parts":[[2022,4,1]],"date-time":"2022-04-01T17:31:59Z","timestamp":1648834319970},"reference-count":0,"publisher":"Tectum \u2013 ein Verlag in der Nomos Verlagsgesellschaft","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2021]]},"DOI":"10.5771\/9783828876873-7","type":"book-chapter","created":{"date-parts":[[2021,3,3]],"date-time":"2021-03-03T12:02:46Z","timestamp":1614772966000},"page":"7-56","source":"Crossref","is-referenced-by-count":0,"title":["B. Das nemo-tenetur-Prinzip im deutschen Strafverfahren"],"prefix":"10.5771","author":[{"given":"Tobias Alexander","family":"Knippel","sequence":"first","affiliation":[]}],"member":"3821","container-title":["Das nemo-tenetur-Prinzip bei au\u00dferstrafrechtlicher Pflichterf\u00fcllung"],"deposited":{"date-parts":[[2021,3,3]],"date-time":"2021-03-03T12:03:11Z","timestamp":1614772991000},"score":16.40071,"resource":{"primary":{"URL":"https:\/\/www.tectum-elibrary.de\/index.php?doi=10.5771\/9783828876873-7"}},"issued":{"date-parts":[[2021]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.5771\/9783828876873-7","published":{"date-parts":[[2021]]}},{"indexed":{"date-parts":[[2022,4,5]],"date-time":"2022-04-05T08:58:58Z","timestamp":1649149138145},"reference-count":0,"publisher":"Osterreichische Akademie der Wissenschaften","content-domain":{"domain":[],"crossmark-restriction":false},"DOI":"10.1553\/0x0028249d","type":"component","created":{"date-parts":[[2019,9,3]],"date-time":"2019-09-03T04:52:00Z","timestamp":1567486320000},"source":"Crossref","is-referenced-by-count":0,"title":["Krause, Otto Hermann; Ps. Nemo"],"prefix":"10.1553","member":"418","deposited":{"date-parts":[[2019,9,3]],"date-time":"2019-09-03T05:21:40Z","timestamp":1567488100000},"score":16.369278,"resource":{"primary":{"URL":"https:\/\/hw.oeaw.ac.at\/ID-0.3031790-1"}},"issued":{"date-parts":[[null]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1553\/0x0028249d"},{"indexed":{"date-parts":[[2022,6,1]],"date-time":"2022-06-01T18:10:45Z","timestamp":1654107045424},"reference-count":14,"publisher":"IEEE","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2006]]},"DOI":"10.1109\/icon.2006.302685","type":"proceedings-article","created":{"date-parts":[[2007,2,28]],"date-time":"2007-02-28T17:16:21Z","timestamp":1172682981000},"source":"Crossref","is-referenced-by-count":2,"title":["Light-NEMO+: Route Optimzation for Light-NEMO Solution"],"prefix":"10.1109","author":[{"given":"M.","family":"Sabeur","sequence":"first","affiliation":[]},{"given":"B.","family":"Jouaber","sequence":"additional","affiliation":[]},{"given":"D.","family":"Zeghlache","sequence":"additional","affiliation":[]}],"member":"263","reference":[{"key":"ref10","article-title":"DHCPv6 Prefix Delegation for NEMO","author":"droms","year":"2004","journal-title":"Internet-Draft draft-droms-nemo-dhcpv6-pd-01"},{"key":"ref11","article-title":"MIRON: MIPv6 Route Optimization for NEMO","author":"bernardos","year":"0","journal-title":"ASWN 2004"},{"key":"ref12","year":"0","journal-title":"Network Simulator 2 (ns2)"},{"key":"ref13","author":"ernst","year":"0","journal-title":"MobiWan NS-2 extensions to Study Mobility in Wide-Area IPv6 Networks"},{"key":"ref14","doi-asserted-by":"publisher","DOI":"10.1109\/CCNC.2006.1593073"},{"key":"ref4","article-title":"Using IPsec to Protect Mobile IPv6 Signaling Between Mobile Nodes and Home Agents","author":"arkko","year":"2004","journal-title":"RFC 3776"},{"key":"ref3","author":"sabeur","year":"2005","journal-title":"MR-proxy based solution for Nested Mobile Network Problems"},{"key":"ref6","article-title":"hierarchical Route Optimization for Nested Mobile Network","author":"lee","year":"0","journal-title":"AINA'04"},{"key":"ref5","article-title":"IPv6 Reverse Routing Header and its application to Mobile Networks","author":"thubert","year":"2004","journal-title":"draft-thubert-nemoreverse-routing-header-04 work-in-progress"},{"key":"ref8","doi-asserted-by":"publisher","DOI":"10.1093\/ietisy\/e89-d.1.158"},{"key":"ref7","article-title":"Hierarchical Mobile IPv6 mobility management (HMIPv6)","author":"soliman","year":"2003","journal-title":"Internet Draft draft-ietf-mobileip—hmipv6–08 txt"},{"key":"ref2","article-title":"IP Mobility Support for IPv6","author":"jhonson","year":"2004","journal-title":"RFC 3775"},{"key":"ref1","article-title":"Network Mobility (NEMO) Basic Support Protocol","author":"devarapalli","year":"2005","journal-title":"RFC 3963"},{"key":"ref9","article-title":"Mobile Network Prefix Delegation extension for Mobile IPv6","author":"paakkonen","year":"2003","journal-title":"Internet-Draft draft-paakkonen-nemo-prefix-delegation-00"}],"event":{"name":"2006 14th IEEE International Conference on Networks","location":"Singapore","start":{"date-parts":[[2006,9,13]]},"end":{"date-parts":[[2006,9,15]]}},"container-title":["2006 14th IEEE International Conference on Networks"],"link":[{"URL":"http:\/\/xplorestaging.ieee.org\/ielx5\/4087640\/4065007\/04087756.pdf?arnumber=4087756","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2017,3,15]],"date-time":"2017-03-15T16:44:59Z","timestamp":1489596299000},"score":16.331953,"resource":{"primary":{"URL":"http:\/\/ieeexplore.ieee.org\/document\/4087756\/"}},"issued":{"date-parts":[[2006]]},"references-count":14,"URL":"http:\/\/dx.doi.org\/10.1109\/icon.2006.302685","published":{"date-parts":[[2006]]}},{"indexed":{"date-parts":[[2022,4,2]],"date-time":"2022-04-02T10:33:36Z","timestamp":1648895616366},"posted":{"date-parts":[[2020,3,23]]},"group-title":"oral","reference-count":0,"publisher":"Copernicus GmbH","content-domain":{"domain":[],"crossmark-restriction":false},"abstract":"\n &lt;p&gt;The Nucleus for European Modelling of the Ocean (NEMO) is a state-of-the art modelling platform for oceanographic research, operational oceanography, sesonnal forecasts and climate studies. NEMO includes three major components; the blue ocean (dynamics), the white ocean (sea-ice), the green ocean (ocean biogeochemistry). It also allows coupling through interfaces with atmosphere (through OASIS software), waves, ice-shelves, so as nesting through the adaptive mesh refinement software AGRIF. Some reference configurations and test cases allowing to explore, to set-up and to validate the applications, and a set of tools to use the platform are also available to the community. The whole platform and its documentation are available under free licence.&lt;\/p&gt;&lt;p&gt;The evolution and reliability of NEMO are organised and controlled by a European Consortium between CMCC (Italy), CNRS (France), MOI France), NOC (UK), UKMO (UK).&lt;\/p&gt;&lt;p&gt;Consortium members agree on long term strategy and yearly plans, sharing expertise and efforts within the NEMO System Team: the core team of NEMO developers in order to ensure the successful and sustainable development of the NEMO System as a well-organised, state-of-the-art ocean model code system suitable for both research and operational work&lt;\/p&gt;\n <\/jats:p>","DOI":"10.5194\/egusphere-egu2020-22213","type":"posted-content","created":{"date-parts":[[2020,3,10]],"date-time":"2020-03-10T06:51:11Z","timestamp":1583823071000},"source":"Crossref","is-referenced-by-count":0,"title":["The NEMO (Nucleus for European Modelling of the Ocean) numerical ocean platform"],"prefix":"10.5194","author":[{"ORCID":"http:\/\/orcid.org\/0000-0001-7481-6369","authenticated-orcid":false,"given":"Guillaume","family":"Samson","sequence":"first","affiliation":[]},{"given":"Claire","family":"Levy","sequence":"additional","affiliation":[]},{"given":"Nemo","family":"System Team","sequence":"additional","affiliation":[]}],"member":"3145","deposited":{"date-parts":[[2020,3,23]],"date-time":"2020-03-23T20:00:37Z","timestamp":1584993637000},"score":16.328957,"resource":{"primary":{"URL":"https:\/\/meetingorganizer.copernicus.org\/EGU2020\/EGU2020-22213.html"}},"issued":{"date-parts":[[2020,3,23]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.5194\/egusphere-egu2020-22213","published":{"date-parts":[[2020,3,23]]},"subtype":"other"},{"indexed":{"date-parts":[[2023,4,21]],"date-time":"2023-04-21T05:32:34Z","timestamp":1682055154069},"reference-count":0,"publisher":"American Institute of Physics","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2013]]},"DOI":"10.1063\/1.4856561","type":"proceedings-article","created":{"date-parts":[[2014,1,3]],"date-time":"2014-01-03T13:38:27Z","timestamp":1388756307000},"source":"Crossref","is-referenced-by-count":2,"title":["Results of the double beta decay experiment NEMO-3"],"prefix":"10.1063","author":[{"given":"V. I.","family":"Tretyak","sequence":"first","affiliation":[]},{"name":"NEMO-3 Collaboration","sequence":"additional","affiliation":[]}],"member":"317","event":{"name":"REVIEW OF PROGRESS IN QUANTITATIVE NONDESTRUCTIVE EVALUATION: Proceedings of the 35th Annual Review of Progress in Quantitative Nondestructive Evaluation","location":"Chicago (Illionois)"},"container-title":["AIP Conference Proceedings"],"deposited":{"date-parts":[[2023,4,20]],"date-time":"2023-04-20T04:42:52Z","timestamp":1681965772000},"score":16.328957,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/aip\/acp\/article\/1572\/1\/110-113\/879701"}},"issued":{"date-parts":[[2013]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1063\/1.4856561","ISSN":["0094-243X"],"issn-type":[{"value":"0094-243X","type":"print"}],"published":{"date-parts":[[2013]]}},{"indexed":{"date-parts":[[2022,4,1]],"date-time":"2022-04-01T07:43:57Z","timestamp":1648799037538},"reference-count":0,"publisher":"Tectum \u2013 ein Verlag in der Nomos Verlagsgesellschaft","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2021]]},"DOI":"10.5771\/9783828876873-63","type":"book-chapter","created":{"date-parts":[[2021,3,3]],"date-time":"2021-03-03T12:02:46Z","timestamp":1614772966000},"page":"63-80","source":"Crossref","is-referenced-by-count":0,"title":["D. Richtungsweisende Rechtsprechung zum nemo-tenetur-Prinzip in Deutschland"],"prefix":"10.5771","author":[{"given":"Tobias Alexander","family":"Knippel","sequence":"first","affiliation":[]}],"member":"3821","container-title":["Das nemo-tenetur-Prinzip bei au\u00dferstrafrechtlicher Pflichterf\u00fcllung"],"deposited":{"date-parts":[[2021,3,3]],"date-time":"2021-03-03T12:03:14Z","timestamp":1614772994000},"score":16.259237,"resource":{"primary":{"URL":"https:\/\/www.tectum-elibrary.de\/index.php?doi=10.5771\/9783828876873-63"}},"issued":{"date-parts":[[2021]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.5771\/9783828876873-63","published":{"date-parts":[[2021]]}},{"indexed":{"date-parts":[[2023,4,21]],"date-time":"2023-04-21T05:19:39Z","timestamp":1682054379104},"reference-count":0,"publisher":"AIP","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2008]]},"DOI":"10.1063\/1.3051951","type":"proceedings-article","created":{"date-parts":[[2009,1,27]],"date-time":"2009-01-27T00:00:58Z","timestamp":1233014458000},"source":"Crossref","is-referenced-by-count":0,"title":["Neutrinoless double beta decay search with the NEMO 3 experiment"],"prefix":"10.1063","author":[{"given":"Irina","family":"Nasteva","sequence":"first","affiliation":[]},{"given":"Pyungwon","family":"Ko","sequence":"additional","affiliation":[]},{"given":"Deog","family":"Ki Hong","sequence":"additional","affiliation":[]},{"name":"NEMO collaboration","sequence":"additional","affiliation":[]}],"member":"317","event":{"name":"SUPERSYMMETRY AND THE UNIFICATION OF FUNDAMENTAL INTERACTIONS","location":"Seoul (Korea)"},"container-title":["AIP Conference Proceedings"],"deposited":{"date-parts":[[2023,4,20]],"date-time":"2023-04-20T08:07:41Z","timestamp":1681978061000},"score":16.15382,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/aip\/acp\/article\/1078\/1\/332-334\/862218"}},"issued":{"date-parts":[[2008]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.1063\/1.3051951","published":{"date-parts":[[2008]]}},{"indexed":{"date-parts":[[2023,2,13]],"date-time":"2023-02-13T22:27:41Z","timestamp":1676327261956},"reference-count":7,"publisher":"Elsevier BV","issue":"1-3","license":[{"start":{"date-parts":[[1999,1,1]],"date-time":"1999-01-01T00:00:00Z","timestamp":915148800000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.elsevier.com\/tdm\/userlicense\/1.0\/"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Nuclear Physics B - Proceedings Supplements"],"published-print":{"date-parts":[[1999,1]]},"DOI":"10.1016\/s0920-5632(98)00427-7","type":"journal-article","created":{"date-parts":[[2002,7,25]],"date-time":"2002-07-25T23:12:27Z","timestamp":1027638747000},"page":"239-241","source":"Crossref","is-referenced-by-count":11,"title":["Double-\u03b2 decays with the NEMO experiment: final results of NEMO-2 with various nuclei and status of NEMO-3"],"prefix":"10.1016","volume":"70","author":[{"given":"Xavier","family":"Sarazin","sequence":"first","affiliation":[]}],"member":"78","reference":[{"key":"10.1016\/S0920-5632(98)00427-7_BIB1","doi-asserted-by":"crossref","unstructured":"NEMO-3 Proposal, LAL preprint 94-29 (1994)","DOI":"10.1093\/cdj\/29.1.94"},{"key":"10.1016\/S0920-5632(98)00427-7_BIB2","doi-asserted-by":"crossref","first-page":"338","DOI":"10.1016\/0168-9002(94)01025-0","volume":"A354","author":"Arnold","year":"1995","journal-title":"Nucl. Instr. Meth."},{"key":"10.1016\/S0920-5632(98)00427-7_BIB3","first-page":"2090","volume":"D51","author":"Dassi\u00e9","year":"1995","journal-title":"Phys. Rev."},{"key":"10.1016\/S0920-5632(98)00427-7_BIB4","first-page":"239","volume":"C72","author":"Arnold","year":"1996","journal-title":"Z. Phys."},{"key":"10.1016\/S0920-5632(98)00427-7_BIB5","unstructured":"Submitted to Nucl. Phys. A"},{"key":"10.1016\/S0920-5632(98)00427-7_BIB6","doi-asserted-by":"crossref","first-page":"221","DOI":"10.1088\/0954-3899\/17\/S\/024","volume":"G17","author":"Manuel","year":"1991","journal-title":"J. Phys."},{"key":"10.1016\/S0920-5632(98)00427-7_BIB7","first-page":"1535","volume":"C46","author":"Elliot","year":"1992","journal-title":"Phys. Rev."}],"container-title":["Nuclear Physics B - Proceedings Supplements"],"language":"en","link":[{"URL":"https:\/\/api.elsevier.com\/content\/article\/PII:S0920563298004277?httpAccept=text\/xml","content-type":"text\/xml","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/api.elsevier.com\/content\/article\/PII:S0920563298004277?httpAccept=text\/plain","content-type":"text\/plain","content-version":"vor","intended-application":"text-mining"}],"deposited":{"date-parts":[[2020,1,29]],"date-time":"2020-01-29T03:26:18Z","timestamp":1580268378000},"score":16.125757,"resource":{"primary":{"URL":"https:\/\/linkinghub.elsevier.com\/retrieve\/pii\/S0920563298004277"}},"issued":{"date-parts":[[1999,1]]},"references-count":7,"journal-issue":{"issue":"1-3","published-print":{"date-parts":[[1999,1]]}},"alternative-id":["S0920563298004277"],"URL":"http:\/\/dx.doi.org\/10.1016\/s0920-5632(98)00427-7","ISSN":["0920-5632"],"issn-type":[{"value":"0920-5632","type":"print"}],"published":{"date-parts":[[1999,1]]}},{"indexed":{"date-parts":[[2022,3,30]],"date-time":"2022-03-30T17:02:18Z","timestamp":1648659738846},"reference-count":0,"publisher":"WORLD SCIENTIFIC","content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2006,4]]},"DOI":"10.1142\/9789812773791_0024","type":"proceedings-article","created":{"date-parts":[[2008,5,27]],"date-time":"2008-05-27T07:19:44Z","timestamp":1211872784000},"source":"Crossref","is-referenced-by-count":0,"title":["THE NEMO ACOUSTIC TEST FACILITY"],"prefix":"10.1142","author":[{"given":"G.","family":"RICCOBENE","sequence":"first","affiliation":[{"name":"Laboratori Nazionali del Sud - INFN, Via S. Sofia 62, Catania 95123, Italy"}]},{"name":"For the NEMO collaboration","sequence":"additional","affiliation":[]}],"member":"219","published-online":{"date-parts":[[2012,1,26]]},"event":{"name":"Proceedings of the International Workshop (ARENA 2005)","location":"DESY, Zeuthen, Germany"},"container-title":["Acoustic and Radio EeV Neutrino Detection Activities"],"deposited":{"date-parts":[[2017,7,12]],"date-time":"2017-07-12T11:21:07Z","timestamp":1499858467000},"score":16.102272,"resource":{"primary":{"URL":"http:\/\/www.worldscientific.com\/doi\/abs\/10.1142\/9789812773791_0024"}},"issued":{"date-parts":[[2006,4]]},"references-count":0,"alternative-id":["10.1142\/9789812773791_0024","10.1142\/6088"],"URL":"http:\/\/dx.doi.org\/10.1142\/9789812773791_0024","published":{"date-parts":[[2006,4]]}},{"indexed":{"date-parts":[[2022,3,29]],"date-time":"2022-03-29T23:41:54Z","timestamp":1648597314504},"reference-count":0,"publisher":"eLife Sciences Publications, Ltd","content-domain":{"domain":[],"crossmark-restriction":false},"DOI":"10.7554\/elife.00785.018","type":"component","created":{"date-parts":[[2013,8,15]],"date-time":"2013-08-15T13:53:59Z","timestamp":1376574839000},"source":"Crossref","is-referenced-by-count":0,"title":["Figure 7. Mutation of NEMO ubiquitination sites does not impair viral induction of interferons."],"prefix":"10.7554","member":"4374","deposited":{"date-parts":[[2018,8,23]],"date-time":"2018-08-23T09:29:29Z","timestamp":1535016569000},"score":16.059391,"resource":{"primary":{"URL":"https:\/\/elifesciences.org\/articles\/00785#fig7"}},"subtitle":["(A\u2013C) Nemo\u2212\/\u2212 MEF cells stably expressing GFP, Flag-NEMO WT or mutants were infected with VSV for the indicated time. Cytokine RNA levels were measured by q-RT-PCR (A and B). Expression of the NEMO proteins was analyzed by immunoblotting (C). (D) A schematic diagram of human NEMO and the ubiquitination sites identified by mass spectrometry. (E and F) Nemo\u2212\/\u2212 MEF cells stably expressing GFP, Flag-NEMO WT, or 6KR (K111\/285\/309\/325\/342\/344R) were infected with VSV for the indicated time. Cytokine RNA levels were analyzed by q-RT-PCR. (G and H) Nemo\u2212\/\u2212 MEF cell extracts (S5) were supplemented with Flag-NEMO WT or 13KR (K111\/139\/143\/165\/283\/285\/321\/325\/326\/342\/344\/399R) protein and incubated with 35S-IRF3 and His6-MAVS\u0394TM. IRF3 dimerization was detected by native gel electrophoresis followed by autoradiography (G). Aliquots of the NEMO proteins were analyzed by immunoblotting (H)."],"issued":{"date-parts":[[null]]},"references-count":0,"URL":"http:\/\/dx.doi.org\/10.7554\/elife.00785.018"}],"items-per-page":20,"query":{"start-index":0,"search-terms":"nemo"}}} \ No newline at end of file +{ + "status": "ok", + "message-type": "work-list", + "message-version": "1.0.0", + "message": { + "facets": {}, + "total-results": 3969, + "items": [ + { + "indexed": { + "date-parts": [[2022, 3, 30]], + "date-time": "2022-03-30T02:49:53Z", + "timestamp": 1648608593454 + }, + "publisher-location": "New York, NY", + "reference-count": 18, + "publisher": "Springer New York", + "license": [ + { + "start": { + "date-parts": [[2020, 1, 1]], + "date-time": "2020-01-01T00:00:00Z", + "timestamp": 1577836800000 + }, + "content-version": "tdm", + "delay-in-days": 0, + "URL": "http:\/\/www.springer.com\/tdm" + }, + { + "start": { + "date-parts": [[2020, 1, 1]], + "date-time": "2020-01-01T00:00:00Z", + "timestamp": 1577836800000 + }, + "content-version": "vor", + "delay-in-days": 0, + "URL": "http:\/\/www.springer.com\/tdm" + }, + { + "start": { + "date-parts": [[2020, 1, 1]], + "date-time": "2020-01-01T00:00:00Z", + "timestamp": 1577836800000 + }, + "content-version": "tdm", + "delay-in-days": 0, + "URL": "http:\/\/www.springer.com\/tdm" + }, + { + "start": { + "date-parts": [[2020, 1, 1]], + "date-time": "2020-01-01T00:00:00Z", + "timestamp": 1577836800000 + }, + "content-version": "vor", + "delay-in-days": 0, + "URL": "http:\/\/www.springer.com\/tdm" + } + ], + "content-domain": { + "domain": ["link.springer.com"], + "crossmark-restriction": false + }, + "published-print": { "date-parts": [[2020]] }, + "DOI": "10.1007\/978-1-4614-8678-7_119", + "type": "book-chapter", + "created": { + "date-parts": [[2020, 11, 4]], + "date-time": "2020-11-04T08:03:17Z", + "timestamp": 1604476997000 + }, + "page": "493-496", + "update-policy": "http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy", + "source": "Crossref", + "is-referenced-by-count": 0, + "title": [ + "NEMO Disease Spectrum Including NEMO-Deleted Exon 5 Autoinflammatory Syndrome (NDAS) and NEMO-Delta C-Terminus (NEMO-DCT)" + ], + "prefix": "10.1007", + "author": [ + { + "given": "Eric P.", + "family": "Hanson", + "sequence": "first", + "affiliation": [] + } + ], + "member": "297", + "published-online": { "date-parts": [[2020, 11, 5]] }, + "reference": [] + } + ] + } +} From 663736120f33a40664961f97fcf297fe86d0692e Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Thu, 14 Aug 2025 15:49:59 +0200 Subject: [PATCH 8/9] WIP: Support serialising JSON as AnyDataValue --- nemo/src/io/formats/json/datavalues/boolean.rs | 2 +- nemo/src/io/formats/json/datavalues/other.rs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/nemo/src/io/formats/json/datavalues/boolean.rs b/nemo/src/io/formats/json/datavalues/boolean.rs index 6a44af738..b193bd327 100644 --- a/nemo/src/io/formats/json/datavalues/boolean.rs +++ b/nemo/src/io/formats/json/datavalues/boolean.rs @@ -23,7 +23,7 @@ pub(crate) mod default { where D: Deserializer<'de>, { - deserializer.deserialize_string(BooleanVisitor {}) + deserializer.deserialize_bool(BooleanVisitor {}) } pub(crate) fn serialize(value: &BooleanDataValue, serializer: S) -> Result diff --git a/nemo/src/io/formats/json/datavalues/other.rs b/nemo/src/io/formats/json/datavalues/other.rs index a54c4de5e..7b3c1bc68 100644 --- a/nemo/src/io/formats/json/datavalues/other.rs +++ b/nemo/src/io/formats/json/datavalues/other.rs @@ -50,6 +50,16 @@ pub(crate) mod default { )) } } + + fn visit_none(self) -> Result + where + E: Error, + { + Ok(OtherDataValue::new( + "null".to_string(), + "http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON".to_string(), + )) + } } pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result From 10f3476841da6d4faacc8d1074a7b0f552e15a03 Mon Sep 17 00:00:00 2001 From: Maximilian Marx Date: Thu, 14 Aug 2025 16:57:56 +0200 Subject: [PATCH 9/9] WIP: Support serialising JSON as AnyDataValue --- nemo/src/io/formats/json/datavalues/other.rs | 41 ++++++++++++++++++-- nemo/src/io/formats/json/variants.rs | 2 +- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/nemo/src/io/formats/json/datavalues/other.rs b/nemo/src/io/formats/json/datavalues/other.rs index 7b3c1bc68..560e66195 100644 --- a/nemo/src/io/formats/json/datavalues/other.rs +++ b/nemo/src/io/formats/json/datavalues/other.rs @@ -51,7 +51,7 @@ pub(crate) mod default { } } - fn visit_none(self) -> Result + fn visit_unit(self) -> Result where E: Error, { @@ -64,9 +64,11 @@ pub(crate) mod default { pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result where - D: Deserializer<'de>, + D: Deserializer<'de> + Copy, { - deserializer.deserialize_string(OtherVisitor {}) + deserializer + .deserialize_string(OtherVisitor {}) + .or_else(|_| deserializer.deserialize_unit(OtherVisitor {})) } pub(crate) fn serialize(value: &T, serializer: S) -> Result @@ -74,6 +76,37 @@ pub(crate) mod default { T: DataValue, S: Serializer, { - serializer.serialize_str(&value.canonical_string()) + if value.datatype_iri() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON" + && value.lexical_value() == "null" + { + serializer.serialize_none() + } else { + serializer.serialize_str(&value.canonical_string()) + } + } + + #[cfg(test)] + mod test { + use std::assert_matches::assert_matches; + + use crate::io::formats::json::variants::default::JsonAnyDataValue; + use nemo_physical::datavalues::{AnyDataValue, DataValue}; + use serde_json::{from_value, Value}; + use test_log::test; + + #[test] + fn null() { + let value: Result = from_value(Value::Null); + assert_matches!(value, Ok(_)); + let value = value.unwrap(); + + assert_matches!(value, JsonAnyDataValue::Other(_)); + let value = AnyDataValue::from(value); + assert_eq!("null", value.lexical_value()); + assert_eq!( + "http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON", + value.datatype_iri() + ); + } } } diff --git a/nemo/src/io/formats/json/variants.rs b/nemo/src/io/formats/json/variants.rs index 75946ceeb..ec6d21f57 100644 --- a/nemo/src/io/formats/json/variants.rs +++ b/nemo/src/io/formats/json/variants.rs @@ -21,7 +21,7 @@ macro_rules! json_variant { use nemo_physical::datavalues::{any_datavalue::AnyDataValueEnum, *}; use crate::io::formats::json::datavalues; - #[derive(Deserialize, Serialize)] + #[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] pub(crate) enum JsonAnyDataValue { $(